Example #1
0
def list_tests(
        completion_context: ICompletionContext) -> List[ITestInfoTypedDict]:
    from robotframework_ls.impl import ast_utils
    from robotframework_ls.impl import robot_constants

    ast = completion_context.get_ast()
    completion_context.check_cancelled()

    ret: List[ITestInfoTypedDict] = []
    node: NodeInfo
    for node in ast_utils.iter_tests(ast):
        completion_context.check_cancelled()
        try:

            test_case_name_token = node.node.header.get_token(
                robot_constants.TESTCASE_NAME)
            if test_case_name_token is None:
                # Old versions have slashes and not spaces as a separator.
                test_case_name_token = node.node.header.get_token(
                    robot_constants.TESTCASE_NAME.replace(" ", "_"))

            ret.append({
                "uri": completion_context.doc.uri,
                "path": completion_context.doc.path,
                "name": test_case_name_token.value,
            })
        except Exception:
            log.exception("Error listing tests in document.")

    return ret
Example #2
0
def _collect_resource_imports_keywords(completion_context: ICompletionContext,
                                       collector):
    """
    :param CompletionContext completion_context:
    """
    for resource_doc in completion_context.get_resource_imports_as_docs():
        new_ctx = completion_context.create_copy(resource_doc)
        _collect_following_imports(new_ctx, collector)
Example #3
0
def _collect_libraries_keywords(
    completion_context: ICompletionContext, collector: IKeywordCollector
):
    """
    :param CompletionContext completion_context:
    """
    # Get keywords from libraries
    from robotframework_ls.impl.robot_constants import BUILTIN_LIB
    from robocorp_ls_core.lsp import CompletionItemKind

    libraries = completion_context.get_imported_libraries()

    library_infos = set(
        _LibInfo(
            completion_context.token_value_resolving_variables(library.name),
            library.alias,
            False,
        )
        for library in libraries
    )
    library_infos.add(_LibInfo(BUILTIN_LIB, None, True))
    libspec_manager = completion_context.workspace.libspec_manager

    for library_info in library_infos:
        completion_context.check_cancelled()
        if not completion_context.memo.complete_for_library(library_info.name):
            continue

        library_doc = libspec_manager.get_library_info(
            library_info.name,
            create=True,
            current_doc_uri=completion_context.doc.uri,
            builtin=library_info.builtin,
        )
        if library_doc is not None:
            #: :type keyword: KeywordDoc
            for keyword in library_doc.keywords:
                keyword_name = keyword.name
                if collector.accepts(keyword_name):

                    keyword_args = []
                    if keyword.args:
                        keyword_args = keyword.args

                    collector.on_keyword(
                        _KeywordFoundFromLibrary(
                            library_doc,
                            keyword,
                            keyword_name,
                            keyword_args,
                            completion_context,
                            CompletionItemKind.Method,
                            library_alias=library_info.alias,
                        )
                    )
Example #4
0
def _collect_following_imports(completion_context: ICompletionContext,
                               collector):
    completion_context.check_cancelled()
    if completion_context.memo.follow_import(completion_context.doc.uri):
        # i.e.: prevent collecting keywords for the same doc more than once.

        _collect_current_doc_keywords(completion_context, collector)

        _collect_resource_imports_keywords(completion_context, collector)

        _collect_libraries_keywords(completion_context, collector)
Example #5
0
def _collect_following_imports(completion_context: ICompletionContext,
                               collector: _Collector):
    completion_context.check_cancelled()
    if completion_context.memo.follow_import_variables(
            completion_context.doc.uri):
        # i.e.: prevent collecting variables for the same doc more than once.

        _collect_current_doc_variables(completion_context, collector)

        _collect_resource_imports_variables(completion_context, collector)

        _collect_variable_imports_variables(completion_context, collector)
Example #6
0
def code_lens_resolve(completion_context: ICompletionContext,
                      code_lens: CodeLensTypedDict):
    # Fill in the command arguments
    command = code_lens.get("command")
    data = code_lens.get("data")
    if command is None and isinstance(
            data, dict) and data.get("type") == "scratchpad":
        from robotframework_ls import import_rf_interactive

        import_rf_interactive()

        code_lens_range: RangeTypedDict = code_lens["range"]
        code_lens_line = code_lens_range["start"]["line"]

        ast = completion_context.get_ast()
        for name_token, header, node in _iter_scratchpad_items(ast):
            if name_token.lineno - 1 == code_lens_line:

                code_lens["command"] = _code_lens_scratchpad_command(
                    header, node, data["uri"])
                break
        else:
            # Unable to resolve
            log.info("Unable to resolve code lens.")
            return code_lens

    return code_lens
def folding_range(
        completion_context: ICompletionContext) -> Optional[List[Dict]]:

    return list([
        x.to_dict() for x in FoldingVisitor.find_from(
            completion_context.get_ast(), completion_context)
    ])
Example #8
0
def signature_help(completion_context: ICompletionContext) -> Optional[dict]:
    keyword_definition = completion_context.get_current_keyword_definition()
    if keyword_definition is not None:

        from robocorp_ls_core.lsp import SignatureHelp
        from robocorp_ls_core.lsp import SignatureInformation
        from robocorp_ls_core.lsp import ParameterInformation

        keyword_found: IKeywordFound = keyword_definition.keyword_found

        keyword_args = keyword_found.keyword_args
        lst = [arg.original_arg for arg in keyword_args]

        label = "%s(%s)" % (keyword_found.keyword_name, ", ".join(lst))
        documentation = keyword_found.docs
        parameters: List[ParameterInformation] = [
            # Note: the label here is to highlight a part of the main signature label!
            # (let's leave this out for now)
            # ParameterInformation("param1", None),
        ]
        signatures: List[SignatureInformation] = [
            SignatureInformation(label, documentation, parameters)
        ]
        return SignatureHelp(signatures,
                             active_signature=0,
                             active_parameter=0).to_dict()

    return None
Example #9
0
def collect_analysis_errors(completion_context: ICompletionContext):
    errors = []
    collector = _KeywordsCollector()
    collect_keywords(completion_context, collector)

    ast = completion_context.get_ast()
    for keyword_usage_info in ast_utils.iter_keyword_usage_tokens(ast):
        completion_context.check_cancelled()
        normalized_name = normalize_robot_name(keyword_usage_info.name)
        if not collector.contains_keyword(normalized_name):

            # There's not a direct match, but the library name may be builtin
            # into the keyword name, so, check if we have a match that way.

            node = keyword_usage_info.node
            error = create_error_from_node(
                node,
                "Undefined keyword: %s." % (keyword_usage_info.name, ),
                tokens=[keyword_usage_info.token],
            )

            errors.append(error)
        else:
            multi = collector.check_multiple_keyword_definitions(
                normalized_name)
            if multi is not None:

                node = keyword_usage_info.node
                error = create_error_from_node(
                    node,
                    "Multiple keywords with name '%s' found. Give the full name of the keyword you want to use:\n%s"
                    % (keyword_usage_info.name, "\n".join([
                        f"    {m.library_alias}.{m.keyword_name}"
                        for m in multi
                    ])),
                    tokens=[keyword_usage_info.token],
                )
                errors.append(error)

        if len(errors) >= MAX_ERRORS:
            # i.e.: Collect at most 100 errors
            break

    errors.extend(CodeAnalysisVisitor.find_from(completion_context))

    return errors
Example #10
0
def _collect_current_doc_variables(completion_context: ICompletionContext,
                                   collector: _Collector):
    """
    :param CompletionContext completion_context:
    """
    # Get keywords defined in the file itself

    ast = completion_context.get_ast()
    _collect_completions_from_ast(ast, completion_context, collector)
Example #11
0
def hover(completion_context: ICompletionContext) -> Optional[dict]:

    t = completion_context.get_current_token()
    if t and t.token and t.token.type == Token.KEYWORD:
        keyword_definition = completion_context.get_current_keyword_definition(
        )
        if keyword_definition is not None:

            from robocorp_ls_core.lsp import Hover

            keyword_found: IKeywordFound = keyword_definition.keyword_found

            signature_args = ""
            for a in keyword_found.keyword_args:
                if signature_args:
                    signature_args += ", "

                if a.is_keyword_arg:
                    signature_args += "**"
                if a.is_star_arg:
                    signature_args += "*"

                signature_args += a.arg_name

                if a.arg_type:
                    signature_args += f": {a.arg_type}"

                if a.default_value:
                    signature_args += f"={a.default_value}"

            signature = f"{keyword_found.keyword_name}({signature_args})"

            hover_text = "```python\n"

            hover_text += signature

            hover_text += "\n```"

            hover_text += "\n"
            hover_text += keyword_found.docs

            return Hover(hover_text).to_dict()

    return None
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_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 []
Example #13
0
def code_lens_scratchpad(
        completion_context: ICompletionContext) -> List[CodeLensTypedDict]:
    from robot.api import Token  # noqa
    from robotframework_ls import import_rf_interactive

    import_rf_interactive()

    ast = completion_context.get_ast()
    completion_context.check_cancelled()

    ret: List[CodeLensTypedDict] = []

    for name_token, header, node in _iter_scratchpad_items(ast):
        completion_context.check_cancelled()
        ret.append(
            _create_scratchpad_code_lens(completion_context, name_token,
                                         header, node))

    return ret
Example #14
0
def folding_range(
    completion_context: ICompletionContext
) -> List[FoldingRangeTypedDict]:
    from robotframework_ls.impl import ast_utils
    from robotframework_ls.impl.protocols import NodeInfo

    ast = completion_context.get_ast()
    completion_context.check_cancelled()

    ret: List[FoldingRangeTypedDict] = []
    node: NodeInfo
    for node in ast_utils.iter_all_nodes(ast):
        completion_context.check_cancelled()
        try:
            start_line = node.node.lineno - 1
            end_line = node.node.end_lineno - 1
            if end_line > start_line:
                ret.append({"startLine": start_line, "endLine": end_line})
        except Exception:
            log.exception("Error computing range")

    return ret
Example #15
0
def collect_variables(completion_context: ICompletionContext,
                      collector: _Collector):
    from robotframework_ls.impl import ast_utils

    token_info = completion_context.get_current_token()
    if token_info is not None:
        if token_info.stack:
            stack_node = token_info.stack[-1]
        else:
            stack_node = completion_context.get_ast_current_section()
        for assign_node_info in ast_utils.iter_variable_assigns(stack_node):
            if collector.accepts(assign_node_info.token.value):
                rep = " ".join(tok.value
                               for tok in assign_node_info.node.tokens)
                variable_found = _VariableFoundFromToken(
                    completion_context, assign_node_info.token, rep)
                collector.on_variable(variable_found)

    _collect_arguments(completion_context, collector)
    _collect_following_imports(completion_context, collector)
    _collect_from_settings(completion_context, collector)
    _collect_from_builtins(completion_context, collector)
Example #16
0
def _collect_completions_from_ast(ast, completion_context: ICompletionContext,
                                  collector):
    from robotframework_ls.impl import ast_utils
    from robocorp_ls_core.lsp import CompletionItemKind

    for keyword in ast_utils.iter_keywords(ast):
        completion_context.check_cancelled()
        keyword_name = keyword.node.name
        if collector.accepts(keyword_name):
            keyword_args = []
            for arg in ast_utils.iter_keyword_arguments_as_str(keyword.node):
                keyword_args.append(KeywordArg(arg))

            collector.on_keyword(
                _KeywordFoundFromAst(
                    ast,
                    keyword.node,
                    keyword_name,
                    keyword_args,
                    completion_context,
                    CompletionItemKind.Function,
                ))
def _collect_completions_from_ast(
    ast, completion_context: ICompletionContext, collector
):
    completion_context.check_cancelled()
    from robotframework_ls.impl import ast_utils
    from robot.api import Token

    ast = completion_context.get_ast()
    for variable_node_info in ast_utils.iter_variables(ast):
        variable_node = variable_node_info.node
        token = variable_node.get_token(Token.VARIABLE)
        if token is None:
            continue
        name = token.value
        if name.endswith("="):
            name = name[:-1].rstrip()

        if collector.accepts(name):
            variable_found = _VariableFoundFromToken(
                completion_context, token, variable_node.value, variable_name=name
            )
            collector.on_variable(variable_found)
Example #18
0
def complete(completion_context: ICompletionContext) -> List[dict]:
    from robotframework_ls.impl.collect_keywords import collect_keywords
    from robotframework_ls.impl import ast_utils

    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 = _Collector(completion_context.sel, token)
            collect_keywords(completion_context, collector)

            return collector.completion_items

    return []
Example #19
0
def code_lens(completion_context: ICompletionContext,
              doc_uri: str) -> Optional[List[Dict]]:
    result = []

    result.append(
        CodeLens(Range(Position(0, 0), Position(0, 0)),
                 Command("Run Suite", "robot.runTestsuite", [doc_uri])))
    result.append(
        CodeLens(Range(Position(0, 0), Position(0, 0)),
                 Command("Debug Suite", "robot.debugTestsuite", [doc_uri])))

    result += CodeLensVisitor.find_from(completion_context.get_ast(), doc_uri,
                                        completion_context)

    return list([x.to_dict() for x in result])
Example #20
0
def _collect_completions_from_ast(ast, completion_context: ICompletionContext,
                                  collector):
    completion_context.check_cancelled()
    from robot.api import Token

    for variable_node_info in completion_context.get_all_variables():
        variable_node = variable_node_info.node
        token = variable_node.get_token(Token.VARIABLE)
        if token is None:
            continue
        name = token.value
        if not name:
            continue
        name = name.strip()
        if not name:
            continue
        if name.endswith("="):
            name = name[:-1].rstrip()

        if name.startswith(("&", "@")):
            # Allow referencing dict(&)/list(@) variables as regular ($) variables
            dict_or_list_var = "$" + name[1:]
            if collector.accepts(dict_or_list_var):
                variable_found = _VariableFoundFromToken(
                    completion_context,
                    token,
                    variable_node.value,
                    variable_name=dict_or_list_var,
                )
                collector.on_variable(variable_found)
        if collector.accepts(name):
            variable_found = _VariableFoundFromToken(completion_context,
                                                     token,
                                                     variable_node.value,
                                                     variable_name=name)
            collector.on_variable(variable_found)
Example #21
0
def _collect_arguments(completion_context: ICompletionContext,
                       collector: _Collector):
    from robotframework_ls.impl import ast_utils

    current_token_info = completion_context.get_current_token()
    if current_token_info is not None:
        stack = current_token_info.stack
        if stack:
            last_in_stack = stack[-1]
            for arg_token in ast_utils.iter_keyword_arguments_as_tokens(
                    last_in_stack):
                name = str(arg_token)
                if collector.accepts(name):
                    variable_found = _VariableFoundFromToken(
                        completion_context, arg_token, "", variable_name=name)
                    collector.on_variable(variable_found)
Example #22
0
def complete(
    completion_context: ICompletionContext,
    imported_keyword_name_to_keyword: Dict[str, List[IKeywordFound]],
):
    from robotframework_ls.impl import ast_utils

    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:
            import_location_info = _obtain_import_location_info(
                completion_context)

            collector = _Collector(
                completion_context.sel,
                token,
                import_location_info,
                imported_keyword_name_to_keyword,
            )
            _collect_auto_import_completions(completion_context, collector)

            return collector.completion_items
    return []
def complete(completion_context: ICompletionContext) -> List[dict]:
    from robotframework_ls.impl.protocols import IKeywordFound
    from robotframework_ls.impl.protocols import IKeywordArg

    ret: List[dict] = []
    sel = completion_context.sel
    if sel.word_from_column:
        # i.e.: if there's any word after the column, skip it (could work, but
        # let's simplify for now).
        return ret

    token_info = completion_context.get_current_token()
    if token_info and token_info.token:
        token = token_info.token

        if token.type not in (token.ARGUMENT, token.EOL):
            return []

    current_keyword_definition = completion_context.get_current_keyword_definition(
    )
    if current_keyword_definition is not None:

        keyword_found: IKeywordFound = current_keyword_definition.keyword_found
        keyword_args = keyword_found.keyword_args
        if keyword_args:
            curr_token_value = token.value

            if "=" in curr_token_value:
                return ret

            # Note: If it's an empty word, it's okay to be in the middle.
            if token.end_col_offset > sel.col and curr_token_value.strip():
                return []

            word_to_column = curr_token_value.strip()

            arg: IKeywordArg
            for arg in keyword_args:
                if arg.is_keyword_arg or arg.is_star_arg:
                    continue

                arg_name = arg.arg_name

                if arg_name.startswith("${") and arg_name.endswith("}"):
                    arg_name = arg_name[2:-1]

                arg_name = arg_name.strip()
                if arg_name:
                    arg_name += "="

                col_start = sel.col
                col_end = sel.col
                new_text = arg_name
                if word_to_column:
                    if not arg_name.startswith(word_to_column):
                        continue
                    new_text = arg_name[len(word_to_column):]

                documentation = arg.original_arg

                ret.append(
                    _create_completion_item(arg_name, new_text, sel, col_start,
                                            col_end, documentation))

    return ret
Example #24
0
def _collect_variable_imports_variables(completion_context: ICompletionContext,
                                        collector: _Collector):
    variable_import_doc: IRobotDocument
    for variable_import_doc in completion_context.get_variable_imports_as_docs(
    ):
        try:
            if variable_import_doc.path.lower().endswith(".py"):
                python_ast = variable_import_doc.get_python_ast()
                if python_ast is not None:
                    import ast as ast_module

                    for node in python_ast.body:
                        if isinstance(node, ast_module.Assign):
                            for target in node.targets:
                                if isinstance(target, ast_module.Name):
                                    varname = "${%s}" % (target.id, )
                                    if collector.accepts(varname):
                                        value = ""
                                        try:
                                            # Only available for Python 3.8 onwards...
                                            end_lineno = getattr(
                                                node.value, "end_lineno", None)
                                            if end_lineno is None:
                                                end_lineno = node.value.lineno

                                            # Only available for Python 3.8 onwards...
                                            end_col_offset = getattr(
                                                node.value, "end_col_offset",
                                                None)
                                            if end_col_offset is None:
                                                end_col_offset = 99999999
                                            value = variable_import_doc.get_range(
                                                node.value.lineno - 1,
                                                node.value.col_offset,
                                                end_lineno - 1,
                                                end_col_offset,
                                            )
                                        except:
                                            log.exception()

                                        variable_found = _VariableFoundFromPythonAst(
                                            variable_import_doc.path,
                                            target.lineno - 1,
                                            target.col_offset,
                                            target.lineno - 1,
                                            target.col_offset + len(target.id),
                                            value,
                                            variable_name=varname,
                                        )
                                        collector.on_variable(variable_found)

            elif variable_import_doc.path.lower().endswith(".yaml"):
                contents = variable_import_doc.get_yaml_contents()
                if isinstance(contents, dict):
                    for key, val in contents.items():
                        key = "${%s}" % (key, )
                        if collector.accepts(key):
                            collector.on_variable(
                                _VariableFoundFromYaml(key, str(val)))

        except:
            log.exception()
Example #25
0
def _collect_resource_imports_variables(completion_context: ICompletionContext,
                                        collector: _Collector):
    resource_doc: IRobotDocument
    for resource_doc in completion_context.get_resource_imports_as_docs():
        new_ctx = completion_context.create_copy(resource_doc)
        _collect_following_imports(new_ctx, collector)
Example #26
0
def code_lens_runs(
        completion_context: ICompletionContext) -> List[CodeLensTypedDict]:
    from robot.api import Token  # noqa
    from robotframework_ls.impl.ast_utils import create_range_from_token

    ast = completion_context.get_ast()
    completion_context.check_cancelled()

    ret: List[CodeLensTypedDict] = []

    start: PositionTypedDict = {"line": 0, "character": 0}
    end: PositionTypedDict = {"line": 0, "character": 0}
    code_lens_range: RangeTypedDict = {"start": start, "end": end}

    sections = ast.sections

    test_case_sections = [
        x for x in sections if x.__class__.__name__ == "TestCaseSection"
    ]

    if len(test_case_sections) > 0:
        # Run Test command
        command: CommandTypedDict = {
            "title":
            "Run Suite",
            "command":
            "robot.runTest",
            "arguments": [{
                "uri": completion_context.doc.uri,
                "path": completion_context.doc.path,
                "name": "*",
            }],
        }

        ret.append({"range": code_lens_range, "command": command})

        # Debug Test command
        command = {
            "title":
            "Debug Suite",
            "command":
            "robot.debugTest",
            "arguments": [{
                "uri": completion_context.doc.uri,
                "path": completion_context.doc.path,
                "name": "*",
            }],
        }

        ret.append({"range": code_lens_range, "command": command})

    for test_case in test_case_sections:
        try:
            for test_node in test_case.body:
                test_case_name_token = test_node.header.get_token(
                    Token.TESTCASE_NAME)
                completion_context.check_cancelled()

                code_lens_range = create_range_from_token(test_case_name_token)

                # Run Test command
                command = {
                    "title":
                    "Run",
                    "command":
                    "robot.runTest",
                    "arguments": [{
                        "uri": completion_context.doc.uri,
                        "path": completion_context.doc.path,
                        "name": test_case_name_token.value,
                    }],
                }

                code_lens_dct: CodeLensTypedDict = {
                    "range": code_lens_range,
                    "command": command,
                }
                ret.append(code_lens_dct)

                # Debug Test command
                command = {
                    "title":
                    "Debug",
                    "command":
                    "robot.debugTest",
                    "arguments": [{
                        "uri": completion_context.doc.uri,
                        "path": completion_context.doc.path,
                        "name": test_node.name,
                    }],
                }

                code_lens_dct = {"range": code_lens_range, "command": command}
                ret.append(code_lens_dct)
        except Exception:
            log.exception("Error computing code lens")

    return ret
Example #27
0
 def find_from(cls, completion_context: ICompletionContext):
     finder = cls(completion_context)
     finder.visit(completion_context.get_ast())
     return finder.errors