def complete(completion_context): """ :param CompletionContext completion_context: """ from robocorp_ls_core.lsp import CompletionItemKind from robocorp_ls_core.lsp import CompletionItem from robocorp_ls_core.lsp import TextEdit from robocorp_ls_core.lsp import Range from robocorp_ls_core.lsp import Position from robotframework_ls.impl import text_utilities from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.robot_lsp_constants import ( OPTION_ROBOT_COMPLETION_SECTION_HEADERS_FORM, ) from robotframework_ls.impl.robot_lsp_constants import ( OPTION_ROBOT_COMPLETION_SECTION_HEADERS_FORM_PLURAL, ) selection = completion_context.sel #: :type selection: DocumentSelection line_start = selection.line_to_column items = [] if line_start: tu = text_utilities.TextUtilities(line_start) if tu.strip_leading_chars("*"): # i.e.: the line must start with '*' tu.strip() words = completion_context.get_accepted_section_header_words() config = completion_context.config form = config.get_setting( OPTION_ROBOT_COMPLETION_SECTION_HEADERS_FORM, str, OPTION_ROBOT_COMPLETION_SECTION_HEADERS_FORM_PLURAL, ) matcher = RobotStringMatcher(tu.text) for word in words: if form == "plural": if not word.endswith("s"): continue elif form == "singular": if word.endswith("s"): continue if matcher.accepts(word): label = "*** %s ***" % (word, ) text_edit = TextEdit( Range( # i.e.: always replace from the start of the line. start=Position(selection.line, 0), end=Position(selection.line, selection.col), ), label, ) # text_edit = None items.append( CompletionItem(label, kind=CompletionItemKind.Class, text_edit=text_edit, insertText=text_edit.newText)) return [item.to_dict() for item in items]
def __init__(self, match_name): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.string_matcher import ( build_matchers_with_resource_or_library_scope, ) self.match_name = match_name self.matches = [] self._matcher = RobotStringMatcher(match_name) self._scope_matchers = build_matchers_with_resource_or_library_scope( match_name)
def __init__(self, selection, token): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.string_matcher import ( build_matchers_with_resource_or_library_scope, ) token_str = token.value self.completion_items = [] self.selection = selection self.token = token self._matcher = RobotStringMatcher(token_str) self._scope_matchers = build_matchers_with_resource_or_library_scope( token_str)
class _FindDefinitionKeywordCollector(IDefinitionsCollector): def __init__(self, match_name): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.string_matcher import ( build_matchers_with_resource_or_library_scope, ) self.match_name = match_name self.matches = [] self._matcher = RobotStringMatcher(match_name) self._scope_matchers = build_matchers_with_resource_or_library_scope( match_name) def accepts(self, keyword_name): return True def on_keyword(self, keyword_found): if any(x.keyword_name == keyword_found.keyword_name for x in self.matches): return if self._matcher.is_keyword_name_match(keyword_found.keyword_name): definition = _DefinitionFromKeyword(keyword_found) self.matches.append(definition) return for matcher in self._scope_matchers: if matcher.is_keyword_match(keyword_found): definition = _DefinitionFromKeyword(keyword_found) self.matches.append(definition) return
def __init__( self, selection, token, import_location_info: "_ImportLocationInfo", imported_keyword_name_to_keyword: Dict[str, List[IKeywordFound]], ): from robotframework_ls.impl.string_matcher import RobotStringMatcher token_str = token.value self.completion_items: List[dict] = [] self.selection = selection self.import_location_info = import_location_info self.token = token self.imported_keyword_name_to_keyword = imported_keyword_name_to_keyword self._matcher = RobotStringMatcher(token_str)
def find_definition(completion_context): """ :param CompletionContext completion_context: :rtype: list(IDefinition) :note: Definitions may be found even if a given source file no longer exists at this place (callers are responsible for validating entries). """ from robotframework_ls.impl import ast_utils from robotframework_ls.impl.collect_keywords import collect_keywords from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.variable_completions import collect_variables token_info = completion_context.get_current_token() if token_info is not None: token = ast_utils.get_keyword_name_token(token_info.node, token_info.token) if token is not None: collector = _FindDefinitionKeywordCollector(token.value) collect_keywords(completion_context, collector) return collector.matches token = ast_utils.get_library_import_name_token( token_info.node, token_info.token) if token is not None: libspec_manager = completion_context.workspace.libspec_manager library_doc = libspec_manager.get_library_info( token.value, create=True, current_doc_uri=completion_context.doc.uri) if library_doc is not None: definition = _DefinitionFromLibrary(library_doc) return [definition] token = ast_utils.get_resource_import_name_token( token_info.node, token_info.token) if token is not None: resource_import_as_doc = completion_context.get_resource_import_as_doc( token_info.node) if resource_import_as_doc is not None: return [_DefinitionFromResource(resource_import_as_doc)] token_info = completion_context.get_current_variable() if token_info is not None: token = token_info.token value = token.value collector = _FindDefinitionVariablesCollector( completion_context.sel, token, RobotStringMatcher(value)) collect_variables(completion_context, collector) return collector.matches return []
def __init__(self, completion_context: ICompletionContext, token): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.string_matcher import ( build_matchers_with_resource_or_library_scope, ) from robotframework_ls.robot_config import create_convert_keyword_format_func token_str = token.value self.completion_items: List[dict] = [] self.completion_context = completion_context self.selection = completion_context.sel self.token = token self._matcher = RobotStringMatcher(token_str) self._scope_matchers = build_matchers_with_resource_or_library_scope( token_str) config = completion_context.config self._convert_keyword_format = create_convert_keyword_format_func( config)
def complete(completion_context): """ :param CompletionContext completion_context: """ from robotframework_ls.impl.string_matcher import RobotStringMatcher token_info = completion_context.get_current_variable() if token_info is not None: token = token_info.token value = token.value if value.endswith("}"): value = value[:-1] collector = _Collector(completion_context.sel, token, RobotStringMatcher(value)) collect_variables(completion_context, collector) return collector.completion_items return []
class _Collector(object): def __init__(self, selection, token): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.string_matcher import ( build_matchers_with_resource_or_library_scope, ) token_str = token.value self.completion_items = [] self.selection = selection self.token = token self._matcher = RobotStringMatcher(token_str) self._scope_matchers = build_matchers_with_resource_or_library_scope( token_str) def accepts(self, keyword_name): if self._matcher.accepts_keyword_name(keyword_name): return True for matcher in self._scope_matchers: if matcher.accepts_keyword_name(keyword_name): return True return False def _create_completion_item_from_keyword(self, keyword_found, selection, token, col_delta=0): """ :param IKeywordFound keyword_found: :param selection: :param token: """ from robotframework_ls.lsp import ( CompletionItem, InsertTextFormat, Position, Range, TextEdit, ) from robotframework_ls.lsp import MarkupKind label = keyword_found.keyword_name text = label for i, arg in enumerate(keyword_found.keyword_args): arg = arg.replace("$", "\\$").replace("{", "").replace("}", "") if arg.startswith("**"): arg = "&" + arg[2:] elif arg.startswith("*"): arg = "@" + arg[1:] text += " ${%s:%s}" % (i + 1, arg) text_edit = TextEdit( Range( start=Position(selection.line, token.col_offset + col_delta), end=Position(selection.line, token.end_col_offset), ), text, ) # text_edit = None return CompletionItem( keyword_found.keyword_name, kind=keyword_found.completion_item_kind, text_edit=text_edit, documentation=keyword_found.docs, insertTextFormat=InsertTextFormat.Snippet, documentationFormat=(MarkupKind.Markdown if keyword_found.docs_format == "markdown" else MarkupKind.PlainText), ).to_dict() def on_keyword(self, keyword_found): col_delta = 0 if not self._matcher.accepts_keyword_name(keyword_found.keyword_name): for matcher in self._scope_matchers: if matcher.accepts_keyword(keyword_found): # +1 for the dot col_delta = len(matcher.resource_or_library_name) + 1 break else: return # i.e.: don't add completion item = self._create_completion_item_from_keyword(keyword_found, self.selection, self.token, col_delta=col_delta) self.completion_items.append(item)
class _Collector(object): def __init__( self, selection, token, import_location_info: "_ImportLocationInfo", imported_keyword_name_to_keyword: Dict[str, List[IKeywordFound]], ): from robotframework_ls.impl.string_matcher import RobotStringMatcher token_str = token.value self.completion_items: List[dict] = [] self.selection = selection self.import_location_info = import_location_info self.token = token self.imported_keyword_name_to_keyword = imported_keyword_name_to_keyword self._matcher = RobotStringMatcher(token_str) def accepts(self, symbols_entry): keyword_name = symbols_entry["name"] if not self._matcher.accepts_keyword_name(keyword_name): return False keywords_found: List[ IKeywordFound] = self.imported_keyword_name_to_keyword.get( keyword_name) if not keywords_found: return True # No longer do this check: if there's some symbol imported already, don't # try to match with the filename (just don't show it). # # This change was done because the check below wasn't perfect # and because it can be a bit confusing since everything imported # works as a wild import in RobotFramework (so, even if it was a different # keyword from a different place, importing it would make it clash and # thus such a completion is kind of strange). # # for keyword_found in keywords_found: # if ( # uris.from_fs_path(keyword_found.source) # == symbols_entry["location"]["uri"] # ): # return False # return True return False def create_completion_item( self, completion_context: ICompletionContext, keyword_name, selection, token, col_delta: int, memo: Set[str], lib_import: Optional[str] = None, resource_path: Optional[str] = None, data: Optional[Any] = None, ) -> None: """ Note: the lib_import and resource_path are the strings to be added so that the given library/resource is loaded. i.e.: It's the name concatenated to the `Library {lib_import}` or `Resource {resource_path}`. """ from robocorp_ls_core.lsp import ( CompletionItem, InsertTextFormat, Position, Range, TextEdit, ) from robocorp_ls_core.lsp import MarkupKind from robotframework_ls.impl.protocols import CompletionType label = f"{keyword_name} ({lib_import or resource_path})" if label in memo: return memo.add(label) prefix = "" import_line = -1 if completion_context.type != CompletionType.shell: if lib_import is not None: import_line = self.import_location_info.get_library_import_line( ) elif resource_path is not None: import_line = self.import_location_info.get_resource_import_line( ) if import_line == -1: # There's no existing import, so, let's see if we have a *** Settings *** section. # If we don't we have to create the whole settings, otherwise, we'll add the statement # as the first thing in the existing *** Settings *** section. if completion_context.type == CompletionType.shell: import_line = 0 prefix = "*** Settings ***\n" elif self.import_location_info.setting_section_node_info is None: import_line = 0 prefix = "*** Settings ***\n" else: import_line = (self.import_location_info. setting_section_node_info.node.end_lineno - 1) text = keyword_name if keyword_name in self.imported_keyword_name_to_keyword: check = lib_import or resource_path if check: basename = os.path.basename(check) if basename.endswith((".txt", ".py", ".robot", ".resource")): basename = os.path.splitext(basename)[0] text = f"{basename}.{keyword_name}" text_edit = TextEdit( Range( start=Position(selection.line, token.col_offset + col_delta), end=Position(selection.line, token.end_col_offset), ), text, ) additional_text_edits: List[TextEdit] = [] if lib_import is not None: additional_text_edits.append( TextEdit( Range(start=Position(import_line, 0), end=Position(import_line, 0)), f"{prefix}Library {lib_import}\n", )) elif resource_path is not None: additional_text_edits.append( TextEdit( Range(start=Position(import_line, 0), end=Position(import_line, 0)), f"{prefix}Resource {resource_path}\n", )) # text_edit = None self.completion_items.append( CompletionItem( label, kind=CompletionItemKind.Reference, text_edit=text_edit, insertText=text_edit.newText, documentation="", insertTextFormat=InsertTextFormat.Snippet, documentationFormat=MarkupKind.PlainText, additionalTextEdits=additional_text_edits, data=data, ).to_dict())
class _Collector(object): def __init__(self, completion_context: ICompletionContext, token): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.string_matcher import ( build_matchers_with_resource_or_library_scope, ) from robotframework_ls.robot_config import create_convert_keyword_format_func token_str = token.value self.completion_items: List[dict] = [] self.completion_context = completion_context self.selection = completion_context.sel self.token = token self._matcher = RobotStringMatcher(token_str) self._scope_matchers = build_matchers_with_resource_or_library_scope( token_str) config = completion_context.config self._convert_keyword_format = create_convert_keyword_format_func( config) def accepts(self, keyword_name): if self._matcher.accepts_keyword_name(keyword_name): return True for matcher in self._scope_matchers: if matcher.accepts_keyword_name(keyword_name): return True return False def _create_completion_item_from_keyword(self, keyword_found: IKeywordFound, selection, token, col_delta=0): from robocorp_ls_core.lsp import ( CompletionItem, InsertTextFormat, Position, Range, TextEdit, ) from robocorp_ls_core.lsp import MarkupKind from robotframework_ls.impl.protocols import IKeywordArg label = keyword_found.keyword_name if keyword_found.library_name: # If we found the keyword in a library, convert its format depending on # the user configuration. label = self._convert_keyword_format(label) text = label arg: IKeywordArg for i, arg in enumerate(keyword_found.keyword_args): if arg.is_keyword_arg or arg.is_star_arg or arg.default_value is not None: continue arg_name = arg.arg_name arg_name = arg_name.replace("$", "\\$").replace("{", "").replace("}", "") text += " ${%s:%s}" % (i + 1, arg_name) text_edit = TextEdit( Range( start=Position(selection.line, token.col_offset + col_delta), end=Position(selection.line, token.end_col_offset), ), text, ) # text_edit = None return CompletionItem( label, kind=keyword_found.completion_item_kind, text_edit=text_edit, insertText=text_edit.newText, documentation=keyword_found.docs, insertTextFormat=InsertTextFormat.Snippet, documentationFormat=(MarkupKind.Markdown if keyword_found.docs_format == "markdown" else MarkupKind.PlainText), ).to_dict() def on_keyword(self, keyword_found): col_delta = 0 if not self._matcher.accepts_keyword_name(keyword_found.keyword_name): for matcher in self._scope_matchers: if matcher.accepts_keyword(keyword_found): # +1 for the dot col_delta = len(matcher.resource_or_library_name) + 1 break else: return # i.e.: don't add completion item = self._create_completion_item_from_keyword(keyword_found, self.selection, self.token, col_delta=col_delta) self.completion_items.append(item)
def complete(completion_context): """ :param CompletionContext completion_context: """ import itertools from robocorp_ls_core.lsp import ( TextEdit, Range, Position, CompletionItem, CompletionItemKind, ) section_name = completion_context.get_current_section_name() if section_name: from robotframework_ls.impl.string_matcher import RobotStringMatcher section = completion_context.get_section(section_name) if section is not None: selection = completion_context.sel #: :type selection: DocumentSelection line_to_col = selection.line_to_column if line_to_col.endswith(" "): return [] replace_to_col = selection.col if section.names_in_brackets: for i, c in enumerate(line_to_col): if c.isspace(): continue elif c == "[": line_to_col = line_to_col[i + 1:] replace_from_col = i break else: return [] else: return [] matcher = RobotStringMatcher(line_to_col) else: # i.e.: Needs to be the first char matcher = RobotStringMatcher(line_to_col) replace_from_col = 0 ret = [] for word in sorted(itertools.chain(section.names, section.aliases)): if matcher.accepts(word): if section.names_in_brackets: label = "[%s]" % (word, ) line = selection.current_line replacement = "[%s]" % (word, ) if line[selection.col:].startswith("]"): replace_to_col += 1 else: label = word replacement = word text_edit = TextEdit( Range( start=Position(selection.line, replace_from_col), end=Position(selection.line, replace_to_col), ), replacement, ) # text_edit = None ret.append( CompletionItem(label, kind=CompletionItemKind.Keyword, text_edit=text_edit).to_dict()) return ret return []
def _get_completions(completion_context, token, match_libs, extensions, skip_current): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robocode_ls_core import uris from robotframework_ls.impl.robot_constants import BUILTIN_LIB ret = [] sel = completion_context.sel value_to_cursor = token.value if token.end_col_offset > sel.col: value_to_cursor = value_to_cursor[:-(token.end_col_offset - sel.col)] if u"{" in value_to_cursor: value_to_cursor = completion_context.token_value_resolving_variables( value_to_cursor) value_to_cursor_split = os.path.split(value_to_cursor) if os.path.isabs(value_to_cursor): _add_completions_from_dir( completion_context, value_to_cursor_split[0], RobotStringMatcher(value_to_cursor_split[1]), ret, sel, token, value_to_cursor_split[1], extensions, skip_current=skip_current, ) else: if match_libs: matcher = RobotStringMatcher(value_to_cursor) libspec_manager = completion_context.workspace.libspec_manager library_names = set(libspec_manager.get_library_names()) library_names.discard(BUILTIN_LIB) for library_name in library_names: if matcher.accepts(library_name): ret.append( _create_completion_item(library_name, sel, token)) # After checking the existing library names in memory (because we # loaded them at least once), check libraries in the filesystem. uri = completion_context.doc.uri path = uris.to_fs_path(uri) dirname = os.path.dirname(path) matcher = RobotStringMatcher(value_to_cursor_split[1]) directory = os.path.join(dirname, value_to_cursor_split[0]) _add_completions_from_dir( completion_context, directory, matcher, ret, sel, token, value_to_cursor_split[1], extensions, skip_current=skip_current, ) return ret
def find_definition( completion_context: ICompletionContext) -> Sequence[IDefinition]: """ :note: Definitions may be found even if a given source file no longer exists at this place (callers are responsible for validating entries). """ from robotframework_ls.impl import ast_utils from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.variable_completions import collect_variables token_info = completion_context.get_current_token() if token_info is not None: matches = find_keyword_definition(completion_context, token_info) if matches is not None: return matches token = ast_utils.get_library_import_name_token( token_info.node, token_info.token) if token is not None: libspec_manager = completion_context.workspace.libspec_manager completion_context.check_cancelled() library_doc = libspec_manager.get_library_info( completion_context.token_value_resolving_variables(token), create=True, current_doc_uri=completion_context.doc.uri, ) if library_doc is not None: definition = _DefinitionFromLibrary(library_doc) return [definition] token = ast_utils.get_resource_import_name_token( token_info.node, token_info.token) if token is not None: completion_context.check_cancelled() resource_import_as_doc = completion_context.get_resource_import_as_doc( token_info.node) if resource_import_as_doc is not None: return [_DefinitionFromResource(resource_import_as_doc)] token = ast_utils.get_variables_import_name_token( token_info.node, token_info.token) if token is not None: completion_context.check_cancelled() variable_import_as_doc = completion_context.get_variable_import_as_doc( token_info.node) if variable_import_as_doc is not None: return [_DefinitionFromVariableImport(variable_import_as_doc)] token_info = completion_context.get_current_variable() if token_info is not None: token = token_info.token value = token.value match = next(iter(_RF_VARIABLE.findall(value)), value) collector = _FindDefinitionVariablesCollector( completion_context.sel, token, RobotStringMatcher(match)) collect_variables(completion_context, collector) return collector.matches return []
class _Collector(object): def __init__(self, selection, token): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robotframework_ls.impl.string_matcher import ( build_matchers_with_resource_or_library_scope, ) token_str = token.value if token is not None else None self.completion_items = [] self.selection = selection self.token = token self._matcher = RobotStringMatcher( token_str) if token_str is not None else None self._scope_matchers = build_matchers_with_resource_or_library_scope( token_str) if token_str is not None else [] def accepts(self, keyword_name): if any(x.label == keyword_name for x in self.completion_items): return False if self._matcher is None: return True if self._matcher.accepts_keyword_name(keyword_name): return True for matcher in self._scope_matchers: if matcher.accepts_keyword_name(keyword_name): return True return False def _create_completion_item_from_keyword(self, keyword_found: IKeywordFound, selection, token, col_delta=0): from robocorp_ls_core.lsp import ( CompletionItem, InsertTextFormat, Position, Range, TextEdit, ) from robocorp_ls_core.lsp import MarkupKind from robotframework_ls.impl.robot_specbuilder import KeywordArg label = keyword_found.keyword_name text = label arg: KeywordArg for i, arg in enumerate(keyword_found.keyword_args): if arg.is_keyword_arg or arg.is_star_arg or arg.default_value is not None: continue arg_name = arg.arg_name arg_name = arg_name.replace("$", "\\$").replace("{", "").replace("}", "") text += " ${%s:%s}" % (i + 1, arg_name) text_edit = TextEdit( Range( start=Position( selection.line, token.col_offset + col_delta if token is not None else selection.col), end=Position( selection.line, token.end_col_offset if token is not None else selection.col), ), text, ) # text_edit = None return CompletionItem( keyword_found.keyword_name, kind=keyword_found.completion_item_kind, text_edit=text_edit, insertText=text_edit.newText, documentation=keyword_found.docs, insertTextFormat=InsertTextFormat.Snippet, documentationFormat=(MarkupKind.Markdown if keyword_found.docs_format == "markdown" else MarkupKind.PlainText), ) def on_keyword(self, keyword_found): col_delta = 0 if self._matcher is not None and not self._matcher.accepts_keyword_name( keyword_found.keyword_name): for matcher in self._scope_matchers: if matcher.accepts_keyword(keyword_found): # +1 for the dot col_delta = len(matcher.resource_or_library_name) + 1 break else: return # i.e.: don't add completion item = self._create_completion_item_from_keyword(keyword_found, self.selection, self.token, col_delta=col_delta) self.completion_items.append(item)