Esempio n. 1
0
    def module_completions(self, module, paths):
        """Directly get completions from the module file

        This is the fallback if all else fails for module completion.
        """
        found = utils.module_search(module, paths)
        if not found:
            return None

        log.debug('Found script for fallback completions: %r', found)
        mod_parts = tuple(re.sub(r'\.+', '.', module).strip('.').split('.'))
        path_parts = os.path.splitext(found)[0].split('/')
        if path_parts[-1] == '__init__':
            path_parts.pop()
        path_parts = tuple(path_parts)
        match_mod = mod_parts
        ml = len(mod_parts)
        for i in range(ml):
            if path_parts[i-ml:] == mod_parts[:ml-i]:
                match_mod = mod_parts[-i:]
                break
        log.debug('Remainder to match: %r', match_mod)

        import jedi
        completions = jedi.api.names(path=found, references=True)
        completions = utils.jedi_walk(completions)
        while len(match_mod):
            for c in completions:
                if c.name == match_mod[0]:
                    completions = c.defined_names()
                    break
            else:
                log.debug('No more matches at %r', match_mod[0])
                return []
            match_mod = match_mod[:-1]

        out = []
        tmp_filecache = {}
        seen = set()
        for c in completions:
            name, type_, desc, abbr = self.parse_completion(c, tmp_filecache)
            seen_key = (type_, name)
            if seen_key in seen:
                continue
            seen.add(seen_key)
            kind = type_ if not self.use_short_types \
                else _types.get(type_) or type_
            out.append((c.module_path, name, type_, desc, abbr, kind))
        return out
Esempio n. 2
0
def cache_context(filename, context, source, extra_path):  # noqa: C901
    """Caching based on context input.

    If the input is blank, it was triggered with `.` to get module completions.

    The module files as reported by Jedi are stored with their modification
    times to help detect if a cache needs to be refreshed.

    For scoped variables in the buffer, construct a cache key using the
    filename.  The buffer file's modification time is checked to see if the
    completion needs to be refreshed.  The approximate scope lines are cached
    to help invalidate the cache based on line position.

    Cache keys are made using tuples to make them easier to interpret later.
    """
    cinput = context['input'].lstrip().lstrip('@')
    if not re.sub(r'[\s\d\.]+', '', cinput):
        return None, []
    filename_hash = hashlib.md5(filename.encode('utf8')).hexdigest()
    line = context['position'][1]
    log.debug('Input: "%s"', cinput)
    cache_key = None
    extra_modules = []
    cur_module = os.path.splitext(os.path.basename(filename))[0]

    if cinput.startswith(('import ', 'from ')):
        # Cache imports with buffer filename as the key prefix.
        # For `from` imports, the first part of the statement is
        # considered to be the same as `import` for caching.

        import_key = 'import~'
        cinput = context['input'].lstrip()
        m = re.search(r'^from\s+(\S+)(.*)', cinput)
        if m:
            if m.group(2).lstrip() in 'import':
                cache_key = ('importkeyword~', )
                return cache_key, extra_modules
            import_key = m.group(1) or 'import~'
        elif cinput.startswith('import ') and cinput.rstrip().endswith('.'):
            import_key = re.sub(r'[^\s\w\.]', ' ', cinput.strip()).split()[-1]

        if import_key:
            if '.' in import_key and import_key[-1] not in whitespace \
                    and not re.search(r'^from\s+\S+\s+import', cinput):
                # Dot completion on the import line
                import_key, _ = import_key.rsplit('.', 1)
            import_key = import_key.rstrip('.')
            module_file = utils.module_search(
                import_key,
                chain(extra_path,
                      [context.get('cwd'), os.path.dirname(filename)],
                      utils.rplugin_runtime_paths(context)))
            if module_file:
                cache_key = (import_key, 'local')
                extra_modules.append(module_file)
            elif is_package(import_key):
                cache_key = (import_key, 'package')
            elif not cinput.endswith('.'):
                cache_key = ('import~',)
            else:
                return None, extra_modules

    if not cache_key:
        obj = split_module(cinput.strip())
        if obj:
            cache_key = (obj, 'package')
            if obj.startswith('self'):
                if os.path.exists(filename):
                    extra_modules.append(filename)
                # `self` is a special case object that needs a scope included
                # in the cache key.
                parents = get_parents(source, line, class_only=True)
                parents.insert(0, cur_module)
                cache_key = (filename_hash, tuple(parents), obj)
            else:
                module_path = full_module(source, obj)
                if module_path and not module_path.startswith('.') \
                        and is_package(module_path):
                    cache_key = (module_path, 'package')
                else:
                    # A quick scan revealed that the dot completion doesn't
                    # involve an imported module.  Treat it like a scoped
                    # variable and ensure the cache invalidates when the file
                    # is saved.
                    if os.path.exists(filename):
                        extra_modules.append(filename)

                    module_file = utils.module_search(module_path,
                                                      [os.path.dirname(filename)])
                    if module_file:
                        cache_key = (module_path, 'local')
                    else:
                        parents = get_parents(source, line)
                        parents.insert(0, cur_module)
                        cache_key = (filename_hash, tuple(parents), obj, 'dot')
        elif context.get('complete_str') or cinput.rstrip().endswith('='):
            parents = get_parents(source, line)
            parents.insert(0, cur_module)
            cache_key = (filename_hash, tuple(parents), 'vars')
            if os.path.exists(filename):
                extra_modules.append(filename)

    return cache_key, extra_modules
Esempio n. 3
0
def cache_context(filename, context, source):
    """Caching based on context input.

    If the input is blank, it was triggered with `.` to get module completions.

    The module files as reported by Jedi are stored with their modification
    times to help detect if a cache needs to be refreshed.

    For scoped variables in the buffer, construct a cache key using the
    filename.  The buffer file's modification time is checked to see if the
    completion needs to be refreshed.  The approximate scope lines are cached
    to help invalidate the cache based on line position.

    Cache keys are made using tuples to make them easier to interpret later.
    """
    cinput = context['input'].lstrip()
    if not re.sub(r'[\s\d\.]+', '', cinput):
        return None, []
    filename_hash = hashlib.md5(filename.encode('utf8')).hexdigest()
    line = context['position'][1]
    log.debug('Input: "%s"', cinput)
    cache_key = None
    extra_modules = []
    cur_module = os.path.splitext(os.path.basename(filename))[0]

    if cinput.startswith(('import ', 'from ')):
        # Cache imports with buffer filename as the key prefix.
        # For `from` imports, the first part of the statement is
        # considered to be the same as `import` for caching.

        import_key = 'import~'
        cinput = context['input'].lstrip()
        m = re.search(r'^from\s+(\S+)', cinput)
        if m:
            import_key = m.group(1) or 'import~'
        elif cinput.startswith('import ') and cinput.rstrip().endswith('.'):
            import_key = re.sub(r'[^\s\w\.]', ' ', cinput.strip()).split()[-1]

        if import_key:
            if '.' in import_key and import_key[-1] not in whitespace \
                    and not re.search(r'^from\s+\S+\s+import', cinput):
                # Dot completion on the import line
                import_key, _ = import_key.rsplit('.', 1)
            import_key = import_key.rstrip('.')
            module_file = utils.module_search(import_key,
                                              [os.getcwd(),
                                               os.path.dirname(filename)])
            if module_file:
                cache_key = (import_key, 'local')
                extra_modules.append(module_file)
            elif is_package(import_key):
                cache_key = (import_key, 'package')
            else:
                cache_key = ('import~',)

    if not cache_key:
        obj = split_module(cinput.strip())
        if obj:
            cache_key = (obj, 'package')
            if obj.startswith('self'):
                if os.path.exists(filename):
                    extra_modules.append(filename)
                # `self` is a special case object that needs a scope included
                # in the cache key.
                parents = get_parents(source, line, class_only=True)
                parents.insert(0, cur_module)
                cache_key = (filename_hash, tuple(parents), obj)
            else:
                module_path = full_module(source, obj)
                if module_path and not module_path.startswith('.') \
                        and is_package(module_path):
                    cache_key = (module_path, 'package')
                else:
                    # A quick scan revealed that the dot completion doesn't
                    # involve an imported module.  Treat it like a scoped
                    # variable and ensure the cache invalidates when the file
                    # is saved.
                    if os.path.exists(filename):
                        extra_modules.append(filename)

                    module_file = utils.module_search(module_path,
                                                      [os.path.dirname(filename)])
                    if module_file:
                        cache_key = (module_path, 'local')
                    else:
                        parents = get_parents(source, line)
                        parents.insert(0, cur_module)
                        cache_key = (filename_hash, tuple(parents), obj, 'dot')
        elif context.get('complete_str') or cinput.rstrip().endswith('='):
            parents = get_parents(source, line)
            parents.insert(0, cur_module)
            cache_key = (filename_hash, tuple(parents), 'vars')
            if os.path.exists(filename):
                extra_modules.append(filename)

    return cache_key, extra_modules