def _create_completion_item(
    label: str,
    new_text: str,
    selection: IDocumentSelection,
    col_start: int,
    col_end: int,
    documentation: str,
) -> dict:
    from robocorp_ls_core.lsp import (
        CompletionItem,
        InsertTextFormat,
        Position,
        Range,
        TextEdit,
    )
    from robocorp_ls_core.lsp import MarkupKind
    from robocorp_ls_core.lsp import CompletionItemKind

    text_edit = TextEdit(
        Range(
            start=Position(selection.line, col_start),
            end=Position(selection.line, col_end),
        ),
        new_text,
    )

    return CompletionItem(
        label,
        kind=CompletionItemKind.Field,
        text_edit=text_edit,
        insertText=label,
        documentation=documentation,
        insertTextFormat=InsertTextFormat.PlainText,
        documentationFormat=MarkupKind.PlainText,
    ).to_dict()
Ejemplo n.º 2
0
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]
Ejemplo n.º 3
0
def test_document_end_of_file_edit():
    old = ["print 'a'\n", "print 'b'\n"]
    doc = Document("file:///uri", u"".join(old))

    change = TextDocumentContentChangeEvent(
        Range(Position(2, 0), Position(2, 0)), 0, u"o")
    doc.apply_change(change)

    assert doc.get_internal_lines() == ("print 'a'\n", "print 'b'\n", "o")
Ejemplo n.º 4
0
def test_document_multiline_edit():
    old = ["def hello(a, b):\n", "    print a\n", "    print b\n"]
    doc = Document("file:///uri", u"".join(old))
    change = TextDocumentContentChangeEvent(
        Range(Position(1, 4), Position(2, 11)), 0, u"print a, b")
    doc.apply_change(change)

    assert doc.get_internal_lines() == ("def hello(a, b):\n",
                                        "    print a, b\n")
Ejemplo n.º 5
0
    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 _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),
        )
Ejemplo n.º 7
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])
Ejemplo n.º 8
0
 def visit_TestCase(self, node: TestCase):
     self.code_lens.append(
         CodeLens(
             Range(Position(node.header.lineno - 1, node.header.col_offset),
                   Position(node.header.lineno - 1,
                            node.header.col_offset)),
             Command("Run Test", "robot.runTestcase",
                     [self.doc_uri, node.name])))
     self.code_lens.append(
         CodeLens(
             Range(Position(node.header.lineno - 1, node.header.col_offset),
                   Position(node.header.lineno - 1,
                            node.header.col_offset)),
             Command("Debug Test", "robot.debugTestcase",
                     [self.doc_uri, node.name])))
def test_rf_interactive_integrated_auto_import_completions(
    language_server_io: ILanguageServerClient,
    rf_interpreter_startup: _RfInterpreterInfo,
    data_regression,
):
    from robocorp_ls_core.workspace import Document
    from robotframework_ls_tests.fixtures import check_code_lens_data_regression

    # Check that we're able to get completions based on the current dir.
    from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_COMPLETIONS
    from robocorp_ls_core.lsp import Position

    uri = rf_interpreter_startup.uri

    language_server = language_server_io
    code = "append to lis"
    doc = Document(uri, code)
    completions = language_server.execute_command(
        ROBOT_INTERNAL_RFINTERACTIVE_COMPLETIONS,
        [{
            "interpreter_id": rf_interpreter_startup.interpreter_id,
            "code": code,
            "position": Position(*doc.get_last_line_col()).to_dict(),
        }],
    )

    suggestions = completions["result"]["suggestions"]
    assert suggestions
    check_code_lens_data_regression(data_regression, suggestions)
Ejemplo n.º 10
0
    def _create_completion_item_from_variable(self, variable_found, selection,
                                              token):
        """
        :param IVariableFound variable_found:
        :param selection:
        :param token:
        """
        from robocorp_ls_core.lsp import (
            CompletionItem,
            InsertTextFormat,
            Position,
            Range,
            TextEdit,
        )
        from robocorp_ls_core.lsp import MarkupKind
        from robocorp_ls_core.lsp import CompletionItemKind

        label = variable_found.variable_name
        text = label
        text = text.replace("$", "\\$")

        text_edit = TextEdit(
            Range(
                start=Position(selection.line, token.col_offset),
                end=Position(selection.line, token.end_col_offset),
            ),
            text,
        )

        # text_edit = None
        return CompletionItem(
            variable_found.variable_name,
            kind=CompletionItemKind.Variable,
            text_edit=text_edit,
            insertText=label,
            documentation=variable_found.variable_value,
            insertTextFormat=InsertTextFormat.Snippet,
            documentationFormat=MarkupKind.PlainText,
        ).to_dict()
def _create_completion_item_from_snippet(label, snippet, selection,
                                         line_to_col):
    """
    :param selection: DocumentSelection
    """
    from robocorp_ls_core.lsp import (
        CompletionItem,
        InsertTextFormat,
        Position,
        Range,
        TextEdit,
    )
    from robocorp_ls_core.lsp import MarkupKind
    from robocorp_ls_core.lsp import CompletionItemKind

    current_col = selection.col

    text = "\n".join(snippet["body"])

    text_edit = TextEdit(
        Range(
            start=Position(selection.line, current_col - len(line_to_col)),
            end=Position(selection.line, current_col),
        ),
        text,
    )

    return CompletionItem(
        label,
        kind=CompletionItemKind.Snippet,
        text_edit=text_edit,
        insertText=text_edit.newText,
        documentation=snippet["description"] +
        "\n".join(["", ""] + snippet["body"]),
        insertTextFormat=InsertTextFormat.Snippet,
        documentationFormat=MarkupKind.Markdown,
    ).to_dict()
def _create_completion_item(library_name,
                            selection,
                            token,
                            start_col_offset=None):
    from robocorp_ls_core.lsp import (
        CompletionItem,
        InsertTextFormat,
        Position,
        Range,
        TextEdit,
    )
    from robocorp_ls_core.lsp import MarkupKind
    from robocorp_ls_core.lsp import CompletionItemKind

    text_edit = TextEdit(
        Range(
            start=Position(
                selection.line,
                start_col_offset
                if start_col_offset is not None else token.col_offset,
            ),
            end=Position(selection.line, token.end_col_offset),
        ),
        library_name,
    )

    # text_edit = None
    return CompletionItem(
        library_name,
        kind=CompletionItemKind.Module,
        text_edit=text_edit,
        insertText=text_edit.newText,
        documentation="",
        insertTextFormat=InsertTextFormat.Snippet,
        documentationFormat=MarkupKind.PlainText,
    ).to_dict()
def test_rf_interactive_integrated_completions(
    language_server_io: ILanguageServerClient,
    rf_interpreter_startup: _RfInterpreterInfo,
):

    from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_COMPLETIONS
    from robocorp_ls_core.lsp import Position

    language_server = language_server_io
    completions = language_server.execute_command(
        ROBOT_INTERNAL_RFINTERACTIVE_COMPLETIONS,
        [{
            "interpreter_id": rf_interpreter_startup.interpreter_id,
            "code": "\n\nLo",
            "position": Position(2, 2).to_dict(),
        }],
    )

    for completion in completions["result"]["suggestions"]:
        if completion["label"] == "Log":
            assert completion == {
                "label": "Log",
                "kind": 0,
                "insertText": "Log    ${1:message}",
                "insertTextRules": 4,
                "documentation":
                "Log(message, level=INFO, html=False, console=False, repr=False, formatter=str)\n\nLogs the given message with the given level.\n\n Valid levels are TRACE, DEBUG, INFO (default), HTML, WARN, and ERROR. Messages below the current active log level are ignored. See [Set Log Level](#Set%20Log%20Level) keyword and --loglevel command line option for more details about setting the level.\n\n Messages logged with the WARN or ERROR levels will be automatically visible also in the console and in the Test Execution Errors section in the log file.\n\n If the html argument is given a true value (see [Boolean arguments](#Boolean%20arguments)), the message will be considered HTML and special characters such as < are not escaped. For example, logging <img src=\"image.png\"> creates an image when html is true, but otherwise the message is that exact string. An alternative to using the html argument is using the HTML pseudo log level. It logs the message as HTML using the INFO level.\n\n If the console argument is true, the message will be written to the console where test execution was started from in addition to the log file. This keyword always uses the standard output stream and adds a newline after the written message. Use [Log To Console](#Log%20To%20Console) instead if either of these is undesirable,\n\n The formatter argument controls how to format the string representation of the message. Possible values are str (default), repr and ascii, and they work similarly to Python built-in functions with same names. When using repr, bigger lists, dictionaries and other containers are also pretty-printed so that there is one item per row. For more details see [String representations](#String%20representations). This is a new feature in Robot Framework 3.1.2.\n\n The old way to control string representation was using the repr argument, and repr=True is still equivalent to using formatter=repr. The repr argument will be deprecated in the future, though, and using formatter is thus recommended.\n\n Examples:\n\n   Log\t Hello, world!\t \t \t \\# Normal INFO message.\t \n  Log\t Warning, world!\t WARN\t \t \\# Warning.\t \n  Log\t <b>Hello</b>, world!\t html=yes\t \t \\# INFO message as HTML.\t \n  Log\t <b>Hello</b>, world!\t HTML\t \t \\# Same as above.\t \n  Log\t <b>Hello</b>, world!\t DEBUG\t html=true\t \\# DEBUG as HTML.\t \n  Log\t Hello, console!\t console=yes\t \t \\# Log also to the console.\t \n  Log\t Null is \\x00\t formatter=repr\t \t \\# Log 'Null is \\x00'.\t \n \n See [Log Many](#Log%20Many) if you want to log multiple messages in one go, and [Log To Console](#Log%20To%20Console) if you only want to write to the console.\n\n",
                "range": {
                    "start": {
                        "line": 5,
                        "character": 4
                    },
                    "end": {
                        "line": 5,
                        "character": 6
                    },
                    "startLineNumber": 3,
                    "startColumn": 1,
                    "endLineNumber": 3,
                    "endColumn": 3,
                },
                "preselect": False,
            }
            break
    else:
        raise AssertionError('Did not find "Log" in the suggestions.')
def test_rf_interactive_integrated_fs_completions(
    language_server_io: ILanguageServerClient,
    rf_interpreter_startup: _RfInterpreterInfo,
    data_regression,
):
    from robocorp_ls_core import uris
    from robocorp_ls_core.workspace import Document

    # Check that we're able to get completions based on the current dir.
    from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_COMPLETIONS
    from robocorp_ls_core.lsp import Position

    uri = rf_interpreter_startup.uri
    fs_path = uris.to_fs_path(uri)
    dirname = os.path.dirname(fs_path)
    with open(os.path.join(dirname, "my_lib_03.py"), "w") as stream:
        stream.write("""
def some_method():
    pass
""")

    language_server = language_server_io
    code = "*** Settings ***\nLibrary    ./my_"
    doc = Document(uri, code)
    completions = language_server.execute_command(
        ROBOT_INTERNAL_RFINTERACTIVE_COMPLETIONS,
        [{
            "interpreter_id": rf_interpreter_startup.interpreter_id,
            "code": code,
            "position": Position(*doc.get_last_line_col()).to_dict(),
        }],
    )

    suggestions = completions["result"]["suggestions"]
    assert suggestions
    data_regression.check(suggestions)
Ejemplo n.º 15
0
    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())
Ejemplo n.º 16
0
def test_document_line_edit():
    doc = Document("file:///uri", u"itshelloworld")
    change = TextDocumentContentChangeEvent(
        Range(Position(0, 3), Position(0, 8)), 0, u"goodbye")
    doc.apply_change(change)
    assert doc.source == u"itsgoodbyeworld"
Ejemplo n.º 17
0
def test_document_empty_edit():
    doc = Document("file:///uri", u"")
    change = TextDocumentContentChangeEvent(
        Range(Position(0, 0), Position(0, 0)), 0, u"f")
    doc.apply_change(change)
    assert doc.source == u"f"
Ejemplo n.º 18
0
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 test_rf_interactive_integrated_basic(
    language_server_io: ILanguageServerClient,
    rf_interpreter_startup: _RfInterpreterInfo,
):
    from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_START
    from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_STOP
    from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_EVALUATE
    from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_SEMANTIC_TOKENS
    from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_COMPLETIONS
    from robocorp_ls_core.lsp import Position

    language_server = language_server_io
    uri = rf_interpreter_startup.uri

    ret2 = language_server.execute_command(ROBOT_INTERNAL_RFINTERACTIVE_START,
                                           [{
                                               "uri": uri
                                           }])
    assert ret2["result"] == {
        "success": True,
        "message": None,
        "result": {
            "interpreter_id": 1
        },
    }

    stop1 = language_server.execute_command(ROBOT_INTERNAL_RFINTERACTIVE_STOP,
                                            [{
                                                "interpreter_id": 0
                                            }])
    assert stop1["result"] == {
        "success": True,
        "message": None,
        "result": None
    }

    stop_inexistant = language_server.execute_command(
        ROBOT_INTERNAL_RFINTERACTIVE_STOP, [{
            "interpreter_id": 22
        }])
    assert stop_inexistant["result"] == {
        "success": False,
        "message": "Did not find interpreter with id: 22",
        "result": None,
    }

    message_matcher = language_server.obtain_pattern_message_matcher(
        {"method": "interpreter/output"})
    eval2 = language_server.execute_command(
        ROBOT_INTERNAL_RFINTERACTIVE_EVALUATE,
        [{
            "interpreter_id":
            1,
            "code":
            """
*** Task ***
Some task
    Log    Something     console=True
""",
        }],
    )
    assert eval2["result"] == {
        "success": True,
        "message": None,
        "result": None
    }
    assert message_matcher.event.wait(10)
    assert message_matcher.msg == {
        "jsonrpc": "2.0",
        "method": "interpreter/output",
        "params": {
            "output": "Something\n",
            "category": "stdout",
            "interpreter_id": 1
        },
    }

    semantic_tokens = language_server.execute_command(
        ROBOT_INTERNAL_RFINTERACTIVE_SEMANTIC_TOKENS,
        [{
            "interpreter_id": 1,
            "code": "Log    Something     console=True"
        }],
    )

    data = semantic_tokens["result"]["data"]
    assert data == [
        0,
        0,
        3,
        7,
        0,
        0,
        7,
        9,
        12,
        0,
        0,
        14,
        7,
        11,
        0,
        0,
        7,
        1,
        6,
        0,
        0,
        1,
        4,
        12,
        0,
    ]

    completions = language_server.execute_command(
        ROBOT_INTERNAL_RFINTERACTIVE_COMPLETIONS,
        [{
            "interpreter_id": 1,
            "code": "Lo",
            "position": Position(0, 2).to_dict()
        }],
    )

    for completion in completions["result"]["suggestions"]:
        if completion["label"] == "Log":
            break
    else:
        raise AssertionError('Did not find "Log" in the suggestions.')

    stop2 = language_server.execute_command(ROBOT_INTERNAL_RFINTERACTIVE_STOP,
                                            [{
                                                "interpreter_id": 1
                                            }])
    assert stop2["result"] == {
        "success": True,
        "message": None,
        "result": None
    }