def target_object_to_completion_item( self, name: str, display_name: str, obj_type: str ) -> CompletionItem: """Convert a target object to its CompletionItem representation.""" key = obj_type if ":" in key: _, key = key.split(":") target_type = COMPLETION_TARGETS.get(key, DEFAULT_TARGET) return CompletionItem( name, kind=target_type.kind, detail=str(display_name), insert_text=target_type.insert_fmt.format(name=name), )
def options_to_completion_items( self, name: str, directive: Directive) -> List[CompletionItem]: """Convert a directive's options to a list of completion items. Unfortunately, the ``autoxxx`` family of directives are a little different. Each ``autoxxxx`` directive name resolves to the same ``AutodocDirective`` class. That paricular directive does not have any options, instead the options are held on the particular Documenter that documents that object type. This method does the lookup in order to determine what those options are. Parameters ---------- name: The name of the directive as it appears in an rst file. directive: The directive whose options we are creating completions for. """ options = directive.option_spec # autoxxx directives require special handlng. if name.startswith("auto") and self.rst.app: self.logger.debug("Processing options for '%s' directive", name) name = name.replace("auto", "") self.logger.debug("Documenter name is '%s'", name) documenter = self.rst.app.registry.documenters.get(name, None) if documenter is not None: options = documenter.option_spec if options is None: return [] return [ CompletionItem( opt, detail="option", kind=CompletionItemKind.Field, insert_text=f"{opt}: ", ) for opt in options ]
def _build_node_completion_item(self, node: XsdNode, order: int = 0) -> CompletionItem: """Generates a completion item with the information about the given node definition. Args: node (XsdNode): The node definition used to build the completion item. order (int): The position for ordering this item. Returns: CompletionItem: The completion item with the basic information about the node. """ return CompletionItem( node.name, CompletionItemKind.Class, documentation=node.get_doc(), sort_text=str(order).zfill(2), )
def lsp_completion_item( completion: Completion, char_before_cursor: str, enable_snippets: bool, resolve_eagerly: bool, markup_kind: MarkupKind, ) -> CompletionItem: """Using a Jedi completion, obtain a jedi completion item.""" completion_name = completion.name name_clean = clean_completion_name(completion_name, char_before_cursor) lsp_type = get_lsp_completion_type(completion.type) completion_item = CompletionItem( label=completion_name, filter_text=completion_name, kind=lsp_type, sort_text=complete_sort_name(completion), insert_text=name_clean, insert_text_format=InsertTextFormat.PlainText, ) _MOST_RECENT_COMPLETIONS[completion_name] = completion if resolve_eagerly: completion_item = lsp_completion_item_resolve( completion_item, markup_kind=markup_kind ) if not enable_snippets: return completion_item if lsp_type not in _LSP_TYPE_FOR_SNIPPET: return completion_item signatures = completion.get_signatures() if not signatures: return completion_item try: snippet_signature = get_snippet_signature(signatures[0]) except Exception: # pylint: disable=broad-except return completion_item new_text = completion_name + snippet_signature completion_item.insertText = new_text completion_item.insertTextFormat = InsertTextFormat.Snippet return completion_item
def _build_attribute_completion_item(self, attr: XsdAttribute, order: int = 0) -> CompletionItem: """Generates a completion item with the information about the given attribute definition. Args: attr (XsdAttribute): The attribute definition used to build the completion item. order (int): The position for ordering this item. Returns: CompletionItem: The completion item with the basic information about the attribute. """ return CompletionItem( attr.name, CompletionItemKind.Variable, documentation=attr.get_doc(), insert_text=f'{attr.name}="$1"', insert_text_format=InsertTextFormat.Snippet, sort_text=str(order).zfill(2), )
def target_to_completion_item(self, label: str, target, target_type: str) -> CompletionItem: key = target_type if ":" in key: key = ":".join(key.split(":")[1:]) completion_type = COMPLETION_TARGETS.get(key, DEFAULT_TARGET) source, version, _, display = target if display == "-": display = label if version: version = f" v{version}" detail = f"{display} - {source}{version}" return CompletionItem(label, kind=completion_type.kind, detail=detail, insert_text=label)
def lsp_completion_item( name: Completion, char_before_cursor: str, enable_snippets: bool, markup_kind: MarkupKind, ) -> CompletionItem: """Using a Jedi completion, obtain a jedi completion item.""" name_name = name.name name_clean = clean_completion_name(name_name, char_before_cursor) lsp_type = get_lsp_completion_type(name.type) completion_item = CompletionItem( label=name_name, filter_text=name_name, kind=lsp_type, detail=name.description, documentation=MarkupContent(kind=markup_kind, value=name.docstring()), sort_text=complete_sort_name(name), insert_text=name_clean, insert_text_format=InsertTextFormat.PlainText, ) if not enable_snippets: return completion_item if lsp_type not in _LSP_TYPE_FOR_SNIPPET: return completion_item signatures = name.get_signatures() if not signatures: return completion_item try: snippet_signature = get_snippet_signature(signatures[0]) except Exception: # pylint: disable=broad-except return completion_item new_text = name_name + snippet_signature completion_item.insertText = new_text completion_item.insertTextFormat = InsertTextFormat.Snippet return completion_item
def directive_to_completion_item(self, name: str, directive: Directive, match: "re.Match", position: Position) -> CompletionItem: """Convert an rst directive to its CompletionItem representation. Previously, it was fine to pre-convert directives into their completion item representation during the :meth:`discover` phase. However a number of factors combined to force this to be something we have to compute specifically for each completion site. It all stems from directives that live under a namespaced domain e.g. ``.. c:macro::``. First in order to get trigger character completions for directives, we need to allow users to start typing the directive name immediately after the second dot and have the CompletionItem insert the leading space. Which is exactly what we used to do, setting ``insert_text=" directive::"`` and we were done. However with domain support, we introduced the possibility of a ``:`` character in the name of a directive. You can imagine a scenario where a user types in a domain namespace, say ``py:`` in order to filter down the list of options to directives that belong to that namespace. With ``:`` being a trigger character for role completions and the like, this would cause editors like VSCode to issue a new completion request ignoring the old one. That isn't necessarily the end of the world, but with CompletionItems assuming that they were following the ``..`` characters, the ``insert_text`` was no longer correct leading to broken completions like ``..py: py:function::``. In order to handle the two scenarios, conceptually the easiest approach is to switch to using a ``text_edit`` and replace the entire line with the correct text. Unfortunately in practice this was rather fiddly. Upon first setting the ``text_edit`` field VSCode suddenly stopped presenting any options! After much debugging, head scratching and searching, I eventually found a `couple <https://github.com/microsoft/vscode/issues/38982>`_ of `issues <https://github.com/microsoft/vscode/issues/41208>`_ that hinted as to what was happening. I **think** what happens is that since the ``range`` of the text edit extends back to the start of the line VSCode considers the entire line to be the filter for the CompletionItems so it's looking to select items that start with ``..`` - which is none of them! To work around this, we additionaly need to set the ``filter_text`` field so that VSCode computes matches against that instead of the label. Then in order for the items to be shown the value of that field needs to be ``..my:directive`` so it corresponds with what the user has actually written. Parameters ---------- name: The name of the directive as a user would type in an reStructuredText document directive: The class definition that implements the Directive's behavior match: The regular expression match object that represents the line we are providing the autocomplete suggestions for. position: The position in the source code where the autocompletion request was sent from. """ groups = match.groupdict() prefix = groups["prefix"] indent = groups["indent"] documentation = inspect.getdoc(directive) # Ignore directives that do not provide their own documentation. if documentation.startswith( "Base class for reStructedText directives."): documentation = None # TODO: Give better names to arguments based on what they represent. args = " ".join("${{{0}:arg{0}}}".format(i) for i in range(1, directive.required_arguments + 1)) return CompletionItem( name, kind=CompletionItemKind.Class, detail="directive", documentation=documentation, filter_text=f"..{prefix}{name}", insert_text_format=InsertTextFormat.Snippet, text_edit=TextEdit( range=Range( Position(position.line, 0), Position(position.line, position.character - 1), ), new_text=f"{indent}.. {name}:: {args}", ), )
def make_keyword_completion_item(word): """Return a LSP::CompletionItem for reserved keyword WORD.""" return CompletionItem(word, CompletionItemKind.Keyword)
def make_builtin_completion_item(word): return CompletionItem(word, CompletionItemKind.Function)
def make_external_completion_item(word): """Return a LSP::CompletionItem for function name WORD.""" return CompletionItem(word, CompletionItemKind.Module)
def make_variable_completion_item(word): """Return a LSP::CompletionItem for variable name WORD.""" return CompletionItem(word, CompletionItemKind.Variable)
def _complete(english_server, params): results = ["what", "no"] return CompletionList(False, [CompletionItem(i) for i in results])
def completions(params: CompletionParams): if ( hasattr(params, "context") and params.context.triggerKind == CompletionTriggerKind.TriggerCharacter ): token = "" trigger = params.context.triggerCharacter else: line = self._cursor_line(params.textDocument.uri, params.position) idx = params.position.character - 1 if 0 <= idx < len(line) and line[idx] in trigger_characters: token = "" trigger = line[idx] else: word = self._cursor_word( params.textDocument.uri, params.position, False ) token = "" if word is None else word[0] trigger = None items: List[CompletionItem] = [] if trigger is None: commands = self._api.search_command(token) items.extend( CompletionItem( x, CompletionItemKind.Function, documentation=self._api.get_command_doc(x), insert_text=x, ) for x in commands ) if trigger is None or trigger == "{": variables = self._api.search_variable(token) items.extend( CompletionItem( x, CompletionItemKind.Variable, documentation=self._api.get_variable_doc(x), insert_text=x, ) for x in variables ) if trigger is None: targets = self._api.search_target(token) items.extend( CompletionItem(x, CompletionItemKind.Class, insert_text=x) for x in targets ) if trigger == "(": func = self._cursor_function(params.textDocument.uri, params.position) if func is not None: func = func.lower() if func == "include": modules = self._api.search_module(token, False) items.extend( CompletionItem( x, CompletionItemKind.Module, documentation=self._api.get_module_doc(x, False), insert_text=x, ) for x in modules ) elif func == "find_package": modules = self._api.search_module(token, True) items.extend( CompletionItem( x, CompletionItemKind.Module, documentation=self._api.get_module_doc(x, True), insert_text=x, ) for x in modules ) return CompletionList(False, items)
def completions(ls, params: CompletionParams = None): print('start') ls.show_message('Validating ' + str(params.position.character)) if params.position.character < 3: print('end') return document = ls.workspace.get_document(params.textDocument.uri) lines = document.lines line = lines[params.position.line] idx = line.rfind('[ch', 0, params.position.character) #print(line) if idx == -1: print('end') return next_char = '' if params.position.character + 1 < len(line): next_char = line[params.position.character + 1] if next_char != ']': next_char = ']' else: next_char = '' in_list = False list_head = line[:idx] line = line[idx:params.position.character] list_bullet = list_head.replace(' ', '') list_stories = [] first_list_head = list_head insert_epic_after = -1 if list_bullet in ['*', '-']: in_list = True def extract_id(search_line: str): line = search_line[1:] # trim the bullet if not line.startswith('[ch'): return None line = line[3:] # Skip `[ch` idx = line.find(']') if idx == -1: return None line = line[:idx] try: return int(line) except: return None first_line = params.position.line search_line = lines[first_line].replace(' ', '') while first_line > 0 and search_line.startswith(list_bullet): first_line -= 1 search_line = lines[first_line].replace(' ', '') story_id = extract_id(search_line) if story_id != None: list_stories.append(story_id) first_line += 1 first_list_head = lines[first_line] idx = first_list_head.find('[') first_list_head = first_list_head[:idx] insert_epic_after = first_line last_line = params.position.line search_line = lines[last_line].replace(' ', '') while last_line + 1 < len(lines) and search_line.replace( ' ', '').startswith(list_bullet): last_line += 1 search_line = lines[last_line].replace(' ', '') story_id = extract_id(search_line) if story_id != None: list_stories.append(story_id) if line.find(']') != -1: print('end') return line = line[3:] print(line) items = [] prec = math.ceil(math.log10(max_story_position)) sort_str = '{}:0{}d{}'.format('{', prec, '}') ordering = 0 prefix = '{}'.format(line) try: id = int(line) except: prefix = '' comp_items = [] for item in all_stories: story = item['story'] if prefix != '': if not str(story['id']).startswith(prefix): continue tag = 'ch{}'.format(story["id"]) label = tag detail = tag + ': ' if item['is_namesake']: detail += 'Epic: ' detail += story["name"] cap = 94 sort_text = '' def sort_conf(is_higher_prio): if is_higher_prio: return '(' return '[' sort_text += sort_conf(item['main_workflow']) sort_text += sort_conf(item['in_progress']) sort_text += sort_conf(item['is_mine']) sort_text += sort_str.format(story['position']) insert_text = tag + next_char epic_insert = [] kind = 'enumMember' if in_list: insert_text += ' ' if item['is_namesake']: insert_text += 'Epic: ' kind = 'enum' insert_text += story['name'] + '\n' + list_head if story['epic_id'] is not None and not item['is_namesake']: namesake = epic_by_id[story['epic_id']]['namesake'] if namesake is not None and namesake['id'] not in list_stories: pos = Position(insert_epic_after, 0) epic_text = '{}[ch{}] Epic: {}\n'.format( first_list_head, namesake['id'], namesake['name']) epic_insert = [TextEdit(Range(pos, pos), epic_text)] if len(detail) > cap: detail = detail[:cap - 3] + '...' comp_items.append( CompletionItem(label=detail, sort_text=sort_text, insert_text=insert_text, additional_text_edits=epic_insert, kind=kind)) print('end') return CompletionList(False, comp_items)
def project_to_completion_item(self, project: str) -> CompletionItem: return CompletionItem(project, detail="intersphinx", kind=CompletionItemKind.Module)