def test_keyword_completions_respect_pythonpath(workspace, cases, libspec_manager, data_regression): from robotframework_ls.impl import keyword_completions from robotframework_ls.impl.completion_context import CompletionContext from robocode_ls_core.config import Config from robotframework_ls.impl.robot_lsp_constants import OPTION_ROBOT_PYTHONPATH case4_path = cases.get_path("case4") # Note how we are accessing case4resource.txt while the workspace is set for case3. config = Config(root_uri="", init_opts={}, process_id=-1, capabilities={}) config.update({"robot": {"pythonpath": [case4_path]}}) assert config.get_setting(OPTION_ROBOT_PYTHONPATH, list, []) == [case4_path] libspec_manager.config = config workspace.set_root(cases.get_path("case3"), libspec_manager=libspec_manager) doc = workspace.get_doc("case3.robot") doc.source = """*** Settings *** Resource case4resource.txt *** Test Cases *** Can use resource keywords [Documentation] Checks that we can have a resource ... including another resource. My Equal Redefined 2 2 Yet Another Equ""" completions = keyword_completions.complete( CompletionContext(doc, workspace=workspace.ws, config=config)) data_regression.check(completions)
def test_keyword_completions_from_resource_files(data_regression, workspace, tmpdir, cases, libspec_manager): from robotframework_ls.impl import keyword_completions from robotframework_ls.impl.completion_context import CompletionContext from robotframework_ls.impl.robot_lsp_constants import OPTION_ROBOT_VARIABLES from robocode_ls_core.config import Config config = Config(root_uri="", init_opts={}, process_id=-1, capabilities={}) config.update( {"robot": { "variables": { "ext_folder": cases.get_path("ext") } }}) assert config.get_setting(OPTION_ROBOT_VARIABLES, dict, {}) == { "ext_folder": cases.get_path("ext") } workspace.set_root(cases.get_path("case3"), libspec_manager=libspec_manager) doc = workspace.get_doc("case3.robot") doc.source = doc.source + "\n equal redef" completions = keyword_completions.complete( CompletionContext(doc, workspace=workspace.ws, config=config)) data_regression.check(completions, basename="keyword_completions_from_resource_files")
def test_resource_does_not_exist(workspace, libspec_manager, data_regression): workspace.set_root("case4", libspec_manager=libspec_manager) doc = workspace.get_doc("case4.robot") doc.source = """*** Settings *** Library DoesNotExist Library . Library .. Library ../ Resource does_not_exist.txt Resource ${foo}/does_not_exist.txt Resource ../does_not_exist.txt Resource . Resource .. Resource ../ Resource ../../does_not_exist.txt Resource case4resource.txt *** Test Cases *** Test case4resource3.Yet Another Equal Redefined""" from robocode_ls_core.config import Config config = Config(root_uri="", init_opts={}, process_id=-1, capabilities={}) # Note: we don't give errors if we can't resolve a resource. _collect_errors(workspace, doc, data_regression, basename="no_error", config=config)
def test_keyword_completions_resource_does_not_exist(workspace, libspec_manager, data_regression): from robocode_ls_core.config import Config from robotframework_ls.impl import keyword_completions from robotframework_ls.impl.completion_context import CompletionContext workspace.set_root("case4", libspec_manager=libspec_manager) doc = workspace.get_doc("case4.robot") doc.source = """*** Settings *** Library DoesNotExist Library . Library .. Library ../ Resource does_not_exist.txt Resource ${foo}/does_not_exist.txt Resource ../does_not_exist.txt Resource . Resource .. Resource ../ Resource ../../does_not_exist.txt Resource case4resource.txt *** Test Cases *** Test case4resource3.""" config = Config(root_uri="", init_opts={}, process_id=-1, capabilities={}) completions = keyword_completions.complete( CompletionContext(doc, workspace=workspace.ws, config=config)) data_regression.check(completions)
def m_initialize( self, processId=None, rootUri=None, rootPath=None, initializationOptions=None, workspaceFolders=None, **_kwargs ): from robocode_ls_core.basic import exit_when_pid_exists from robocode_ls_core.lsp import WorkspaceFolder from robocode_ls_core.config import Config log.debug( "Language server initialized with:\n processId: %s\n rootUri: %s\n rootPath: %s\n initializationOptions: %s\n workspaceFolders: %s", processId, rootUri, rootPath, initializationOptions, workspaceFolders, ) if rootUri is None: rootUri = uris.from_fs_path(rootPath) if rootPath is not None else "" self.root_uri = rootUri self.config = Config( rootUri, initializationOptions or {}, processId, _kwargs.get("capabilities", {}), ) if workspaceFolders: workspaceFolders = [WorkspaceFolder(**w) for w in workspaceFolders] self.workspace = self._create_workspace(rootUri, workspaceFolders or []) if processId not in (None, -1, 0): exit_when_pid_exists(processId) # Get our capabilities return {"capabilities": self.capabilities()}
def test_section_completions(data_regression): from robotframework_ls.impl import section_completions from robotframework_ls.impl.completion_context import CompletionContext from robotframework_ls.impl.robot_workspace import RobotDocument from robocode_ls_core.config import Config config = Config(root_uri="", init_opts={}, process_id=-1, capabilities={}) config.update({"robot": {"completions": {"section_headers": {"form": "both"}}}}) doc = RobotDocument("unused", source="""**""") completions = section_completions.complete(CompletionContext(doc, config=config)) data_regression.check(completions, basename="header_completions_all") doc = RobotDocument("unused", source="""**settin""") completions = section_completions.complete(CompletionContext(doc, config=config)) data_regression.check(completions, basename="header_completions_filter_settings") config.update({}) doc = RobotDocument("unused", source="""**""") completions = section_completions.complete(CompletionContext(doc, config=config)) data_regression.check(completions, basename="header_completions_all_plural")
def _create_config(self) -> IConfig: from robocode_ls_core.config import Config return Config(all_options=frozenset())
def test_config(tmpdir): from robotframework_ls.impl.robot_lsp_constants import ( OPTION_ROBOT_PYTHON_EXECUTABLE, ) from robocode_ls_core.config import Config from robotframework_ls.impl.robot_lsp_constants import ( OPTION_ROBOT_LANGUAGE_SERVER_TCP_PORT, ) config = Config(root_uri=str(tmpdir), init_opts={}, process_id=None, capabilities={}) settings = { "robot": { "language-server": { "tcp-port": 1456, "args": ["-vv", "--log-file=~/robotframework_ls.log"], }, "python": { "executable": "foobar", "value": "10", "value_float": "10.5" }, } } config.update(settings) assert config.get_setting(OPTION_ROBOT_PYTHON_EXECUTABLE, str) == "foobar" assert config.get_setting(OPTION_ROBOT_LANGUAGE_SERVER_TCP_PORT, int) == 1456 # i.e.: convert to type when possible assert config.get_setting("robot.python.value", int) == 10 assert config.get_setting("robot.python.value_float", float) == 10.5 with pytest.raises(KeyError): config.get_setting("robot.python.value_float", int) with pytest.raises(KeyError): assert config.get_setting("robot.not_there", int)
class PythonLanguageServer(MethodDispatcher): """ Implementation of the Microsoft VSCode Language Server Protocol https://github.com/Microsoft/language-server-protocol/blob/master/versions/protocol-1-x.md Based on: https://github.com/palantir/python-language-server/blob/develop/pyls/python_ls.py """ def __init__(self, read_stream, write_stream, max_workers=MAX_WORKERS): from robocode_ls_core.lsp import LSPMessages self.workspace = None self.config = None self.root_uri = None self.watching_thread = None self.uri_workspace_mapper = {} self._jsonrpc_stream_reader = JsonRpcStreamReader(read_stream) self._jsonrpc_stream_writer = JsonRpcStreamWriter(write_stream) self._endpoint = Endpoint( self, self._jsonrpc_stream_writer.write, max_workers=max_workers ) self._lsp_messages = LSPMessages(self._endpoint) self._shutdown = False def start(self): """Entry point for the server.""" self._jsonrpc_stream_reader.listen(self._endpoint.consume) def m_shutdown(self, **_kwargs): self._shutdown = True return None def m_exit(self, **_kwargs): self._endpoint.shutdown() # If there's someone reading, we could deadlock here. self._jsonrpc_stream_reader.close() self._jsonrpc_stream_writer.close() def capabilities(self): return {} # Subclasses should override for capabilities. def m_initialize( self, processId=None, rootUri=None, rootPath=None, initializationOptions=None, workspaceFolders=None, **_kwargs ): from robocode_ls_core.basic import exit_when_pid_exists from robocode_ls_core.lsp import WorkspaceFolder from robocode_ls_core.config import Config log.debug( "Language server initialized with:\n processId: %s\n rootUri: %s\n rootPath: %s\n initializationOptions: %s\n workspaceFolders: %s", processId, rootUri, rootPath, initializationOptions, workspaceFolders, ) if rootUri is None: rootUri = uris.from_fs_path(rootPath) if rootPath is not None else "" self.root_uri = rootUri self.config = Config( rootUri, initializationOptions or {}, processId, _kwargs.get("capabilities", {}), ) if workspaceFolders: workspaceFolders = [WorkspaceFolder(**w) for w in workspaceFolders] self.workspace = self._create_workspace(rootUri, workspaceFolders or []) if processId not in (None, -1, 0): exit_when_pid_exists(processId) # Get our capabilities return {"capabilities": self.capabilities()} def _create_workspace(self, root_uri, workspace_folders): from robocode_ls_core.workspace import Workspace return Workspace(root_uri, workspace_folders) def m_initialized(self, **_kwargs): pass def lint(self, doc_uri, is_saved): raise NotImplementedError( "Subclasses must override (current class: %s)." % (self.__class__,) ) def m_text_document__did_close(self, textDocument=None, **_kwargs): self.workspace.remove_document(textDocument["uri"]) def m_text_document__did_open(self, textDocument=None, **_kwargs): from robocode_ls_core.lsp import TextDocumentItem self.workspace.put_document(TextDocumentItem(**textDocument)) self.lint(textDocument["uri"], is_saved=True) def m_text_document__did_change( self, contentChanges=None, textDocument=None, **_kwargs ): from robocode_ls_core.lsp import TextDocumentItem from robocode_ls_core.lsp import TextDocumentContentChangeEvent if contentChanges: text_document_item = TextDocumentItem(**textDocument) for change in contentChanges: try: range = change.get("range", None) range_length = change.get("rangeLength", 0) text = change.get("text", "") self.workspace.update_document( text_document_item, TextDocumentContentChangeEvent( range=range, rangeLength=range_length, text=text ), ) except: log.exception( "Error updating document: %s with changes: %s" % (textDocument, contentChanges) ) self.lint(textDocument["uri"], is_saved=False) def m_text_document__did_save(self, textDocument=None, **_kwargs): self.lint(textDocument["uri"], is_saved=True) def m_workspace__did_change_configuration(self, settings=None): self.config.update(settings or {}) def m_workspace__did_change_workspace_folders(self, event): """Adds/Removes folders from the workspace.""" from robocode_ls_core.lsp import WorkspaceFolder log.info("Workspace folders changed: {}".format(event)) added_folders = event["added"] or [] removed_folders = event["removed"] or [] for f_add in added_folders: self.workspace.add_folder(WorkspaceFolder(**f_add)) for f_remove in removed_folders: self.workspace.remove_folder(f_remove["uri"]) def m_workspace__did_change_watched_files(self, changes=None, **_kwargs): pass