def test_customize_interpreter(language_server_io: ILanguageServerClient, workspace_dir: str, cases: CasesFixture): from robocode_ls_core import uris import os from pathlib import Path from robotframework_ls.impl.robot_workspace import RobotDocument language_server = language_server_io cases.copy_to("custom_env", workspace_dir) language_server.initialize(workspace_dir, process_id=os.getpid()) case1_robot: Path = Path(workspace_dir) / "env1" / "caselib1.robot" assert case1_robot.exists() uri_case1 = uris.from_fs_path(str(case1_robot)) doc = RobotDocument(uri_case1) i_line = doc.find_line_with_contents(" verify lib1") language_server.open_doc(uri_case1, 1) ret = language_server.find_definitions(uri_case1, i_line, 6) result = ret["result"] assert not result # Now, customize it with the plugins. plugins_dir = cases.get_path("custom_env/plugins") add_plugins_result = language_server.execute_command( "robot.addPluginsDir", [plugins_dir]) assert add_plugins_result["result"] ret = language_server.find_definitions(uri_case1, i_line, 6) result = ret["result"] assert result check = next(iter(result)) assert check["uri"].endswith("lib1.py") # Check with another case case2_robot: Path = Path(workspace_dir) / "env2" / "caselib2.robot" assert case2_robot.exists() uri_case2 = uris.from_fs_path(str(case2_robot)) doc = RobotDocument(uri_case2) i_line = doc.find_line_with_contents(" verify lib2") ret = language_server.find_definitions(uri_case2, i_line, 6) result = ret["result"] assert result check = next(iter(result)) assert check["uri"].endswith("lib2.py")
def test_find_definition_integrated_library(language_server, cases, tmpdir): from robocode_ls_core import uris workspace_dir = str(tmpdir.join("workspace")) cases.copy_to("case1", workspace_dir) language_server.initialize(workspace_dir, process_id=os.getpid()) case1_robot = os.path.join(workspace_dir, "case1.robot") assert os.path.exists(case1_robot) uri = uris.from_fs_path(case1_robot) language_server.open_doc(uri, 1, text=None) ret = language_server.find_definitions(uri, 5, 6) result = ret["result"] assert len(result) == 1 check = next(iter(result)) assert check["uri"].endswith("case1_library.py") assert check["range"] == { "start": { "line": 7, "character": 0 }, "end": { "line": 7, "character": 0 }, }
def test_typing_not_shown(libspec_manager, tmpdir, workspace, data_regression): from robocode_ls_core import uris from os.path import os from robotframework_ls_tests.fixtures import LIBSPEC_3 from robotframework_ls.impl import keyword_completions from robotframework_ls.impl.completion_context import CompletionContext ws_dir = str(tmpdir.join("workspace_dir_a")) os.mkdir(ws_dir) with open(os.path.join(ws_dir, "my.libspec"), "w") as stream: stream.write(LIBSPEC_3) libspec_manager.add_workspace_folder(uris.from_fs_path(ws_dir)) assert libspec_manager.get_library_info("case3_library", create=False) is not None workspace.set_root(str(tmpdir), libspec_manager=libspec_manager) doc = workspace.get_doc("temp_doc.robot") doc.source = """*** Settings *** Library case3_library *** Test Cases *** Can use resource keywords Case Verify""" completions = keyword_completions.complete( CompletionContext(doc, workspace=workspace.ws)) data_regression.check(completions)
def test_find_definition_keywords(language_server: ILanguageServerClient, cases, workspace_dir): from robocode_ls_core import uris cases.copy_to("case2", workspace_dir) language_server.initialize(workspace_dir, process_id=os.getpid()) case2_robot = os.path.join(workspace_dir, "case2.robot") assert os.path.exists(case2_robot) uri = uris.from_fs_path(case2_robot) language_server.open_doc(uri, 1, text=None) ret = language_server.find_definitions(uri, 7, 6) result = ret["result"] assert len(result) == 1 check = next(iter(result)) assert check["uri"].endswith("case2.robot") assert check["range"] == { "start": { "line": 1, "character": 0 }, "end": { "line": 4, "character": 5 }, }
def m_workspace__execute_command(self, command=None, arguments=()) -> Any: if command == "robot.addPluginsDir": directory: str = arguments[0] assert os.path.isdir( directory), f"Expected: {directory} to be a directory." self._pm.load_plugins_from(Path(directory)) return True elif command == "robot.resolveInterpreter": try: from robocode_ls_core import uris from robotframework_ls.ep_resolve_interpreter import ( EPResolveInterpreter, ) from robotframework_ls.ep_resolve_interpreter import IInterpreterInfo target_robot: str = arguments[0] for ep in self._pm.get_implementations(EPResolveInterpreter): interpreter_info: IInterpreterInfo = ep.get_interpreter_info_for_doc_uri( uris.from_fs_path(target_robot)) if interpreter_info is not None: return { "pythonExe": interpreter_info.get_python_exe(), "environ": interpreter_info.get_environ(), "additionalPythonpathEntries": interpreter_info.get_additional_pythonpath_entries( ), } except: log.exception( f"Error resolving interpreter. Args: {arguments}")
def test_resolve_interpreter( cases: CasesFixture, config_provider: IConfigProvider, rcc_conda_installed ) -> None: from robocode_ls_core.constants import NULL from robocode_vscode.plugins.resolve_interpreter import RobocodeResolveInterpreter from robocode_ls_core import uris from robocode_ls_core.pluginmanager import PluginManager from robotframework_ls.ep_providers import EPConfigurationProvider from robotframework_ls.ep_providers import EPEndPointProvider from pathlib import Path from robocode_vscode.plugins.resolve_interpreter import _CacheInfo _CacheInfo._cache_hit_files = 0 pm = PluginManager() pm.set_instance(EPConfigurationProvider, config_provider) pm.set_instance(EPEndPointProvider, NULL) resolve_interpreter = RobocodeResolveInterpreter(weak_pm=weakref.ref(pm)) path = cases.get_path( "custom_envs/simple-web-scraper/tasks/simple-web-scraper.robot" ) interpreter_info = resolve_interpreter.get_interpreter_info_for_doc_uri( uris.from_fs_path(path) ) assert interpreter_info assert os.path.exists(interpreter_info.get_python_exe()) assert interpreter_info.get_environ() additional_pythonpath_entries = interpreter_info.get_additional_pythonpath_entries() assert len(additional_pythonpath_entries) == 3 found = set() for v in additional_pythonpath_entries: p = Path(v) assert p.is_dir() found.add(p.name) assert found == {"variables", "libraries", "resources"} assert _CacheInfo._cache_hit_files == 0 assert _CacheInfo._cache_hit_interpreter == 0 interpreter_info = resolve_interpreter.get_interpreter_info_for_doc_uri( uris.from_fs_path(path) ) assert _CacheInfo._cache_hit_files == 2 assert _CacheInfo._cache_hit_interpreter == 1
def test_document_from_file(workspace, workspace_dir, cases): from os.path import os from robocode_ls_core import uris from robocode_ls_core.lsp import TextDocumentItem cases.copy_to("case1", workspace_dir) workspace.set_root(workspace_dir) ws = workspace.ws case1_file = os.path.join(workspace_dir, "case1.robot") assert os.path.exists(case1_file) case1_doc_uri = uris.from_fs_path(case1_file) resource_doc = ws.get_document(case1_doc_uri, accept_from_file=False) assert resource_doc is None cached_doc = ws.get_document(case1_doc_uri, accept_from_file=True) assert cached_doc is not None assert "*** Settings ***" in cached_doc.source with open(case1_file, "w") as stream: stream.write("new contents") assert "*** Settings ***" in cached_doc.source # i.e.: Unchanged # When we get it again it verifies the filesystem. cached_doc2 = ws.get_document(case1_doc_uri, accept_from_file=True) assert cached_doc is cached_doc2 assert cached_doc.source == "new contents" # Still None if we can't accept cached. resource_doc = ws.get_document(case1_doc_uri, accept_from_file=False) assert resource_doc is None ws.put_document(TextDocumentItem(case1_doc_uri, text="rara")) resource_doc = ws.get_document(case1_doc_uri, accept_from_file=False) assert resource_doc is not None assert resource_doc is not cached_doc ws.remove_document(case1_doc_uri) resource_doc = ws.get_document(case1_doc_uri, accept_from_file=False) assert resource_doc is None cached_doc3 = ws.get_document(case1_doc_uri, accept_from_file=True) assert cached_doc3 is not None # i.e.: it should've been pruned when the doc was added. assert cached_doc3 is not cached_doc assert cached_doc3.source == "new contents" os.remove(case1_file) cached_doc4 = ws.get_document(case1_doc_uri, accept_from_file=True) assert cached_doc4 is None # The old one in memory doesn't change after the file is removed assert cached_doc3.source == "new contents"
def change_workspace_folders(self, added_folders: List[str], removed_folders: List[str]) -> None: from robocode_ls_core import uris import os.path added_folders_uri_name = [{ "uri": uris.from_fs_path(s), "name": os.path.basename(s) } for s in added_folders] removed_folders_uri_name = [{ "uri": uris.from_fs_path(s), "name": os.path.basename(s) } for s in removed_folders] self.write({ "jsonrpc": "2.0", "method": "workspace/didChangeWorkspaceFolders", "params": { "event": { "added": added_folders_uri_name, "removed": removed_folders_uri_name, } }, })
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 get_resource_import_as_doc(self, resource_import): from robocode_ls_core import uris import os.path from robotframework_ls.impl.robot_lsp_constants import OPTION_ROBOT_PYTHONPATH ws = self._workspace for token in resource_import.tokens: if token.type == token.NAME: name_with_resolved_vars = self.token_value_resolving_variables( token) if not os.path.isabs(name_with_resolved_vars): # It's a relative resource, resolve its location based on the # current file. check_paths = [ os.path.join(os.path.dirname(self.doc.path), name_with_resolved_vars) ] config = self.config if config is not None: for additional_pythonpath_entry in config.get_setting( OPTION_ROBOT_PYTHONPATH, list, []): check_paths.append( os.path.join(additional_pythonpath_entry, name_with_resolved_vars)) else: check_paths = [name_with_resolved_vars] for resource_path in check_paths: doc_uri = uris.from_fs_path(resource_path) resource_doc = ws.get_document(doc_uri, accept_from_file=True) if resource_doc is None: log.info("Resource not found: %s", resource_path) continue return resource_doc return None
def m_find_definition(self, doc_uri, line, col): from robotframework_ls.impl.find_definition import find_definition import os.path from robocode_ls_core.lsp import Location, Range from robocode_ls_core import uris completion_context = self._create_completion_context( doc_uri, line, col) if completion_context is None: return None definitions = find_definition(completion_context) ret = [] for definition in definitions: if not definition.source: log.info("Found definition with empty source (%s).", definition) continue if not os.path.exists(definition.source): log.info("Found definition: %s (but source does not exist).", definition) continue lineno = definition.lineno if lineno is None or lineno < 0: lineno = 0 end_lineno = definition.end_lineno if end_lineno is None or end_lineno < 0: end_lineno = 0 col_offset = definition.col_offset end_col_offset = definition.end_col_offset ret.append( Location( uris.from_fs_path(definition.source), Range((lineno, col_offset), (end_lineno, end_col_offset)), ).to_dict()) return ret
def get_resource_imports_as_docs(self): from robocode_ls_core import uris import os.path from robotframework_ls.impl import ast_utils from robotframework_ls.impl.robot_lsp_constants import OPTION_ROBOT_PYTHONPATH ws = self._workspace ret = [] # Get keywords from resources resource_imports = self.get_resource_imports() for resource_import in resource_imports: for token in resource_import.tokens: if token.type == token.NAME: parts = [] for v in ast_utils.tokenize_variables(token): if v.type == v.NAME: parts.append(str(v)) elif v.type == v.VARIABLE: # Resolve variable from config v = str(v) if v.startswith("${") and v.endswith("}"): v = v[2:-1] parts.append(self.convert_robot_variable(v)) else: log.info("Cannot resolve variable: %s", v) joined_parts = "".join(parts) if not os.path.isabs(joined_parts): # It's a relative resource, resolve its location based on the # current file. check_paths = [ os.path.join(os.path.dirname(self.doc.path), joined_parts) ] config = self.config if config is not None: for additional_pythonpath_entry in config.get_setting( OPTION_ROBOT_PYTHONPATH, list, []): check_paths.append( os.path.join(additional_pythonpath_entry, joined_parts)) else: check_paths = [joined_parts] for resource_path in check_paths: if not os.path.isfile(resource_path): log.info("Resource not found: %s", resource_path) continue doc_uri = uris.from_fs_path(resource_path) resource_doc = ws.get_document(doc_uri, create=False) if resource_doc is None: resource_doc = ws.create_untracked_document( doc_uri) ret.append(resource_doc) break return tuple(ret)
def initialize(self, root_path, msg_id=None, process_id=None): from robocode_ls_core.uris import from_fs_path msg_id = msg_id if msg_id is not None else self.next_id() msg = self.request({ "jsonrpc": "2.0", "id": msg_id, "method": "initialize", "params": { "processId": process_id, "rootPath": root_path, "rootUri": from_fs_path(root_path), "capabilities": { "workspace": { "applyEdit": True, "didChangeConfiguration": { "dynamicRegistration": True }, "didChangeWatchedFiles": { "dynamicRegistration": True }, "symbol": { "dynamicRegistration": True }, "executeCommand": { "dynamicRegistration": True }, }, "textDocument": { "synchronization": { "dynamicRegistration": True, "willSave": True, "willSaveWaitUntil": True, "didSave": True, }, "completion": { "dynamicRegistration": True, "completionItem": { "snippetSupport": True, "commitCharactersSupport": True, }, }, "hover": { "dynamicRegistration": True }, "signatureHelp": { "dynamicRegistration": True }, "definition": { "dynamicRegistration": True }, "references": { "dynamicRegistration": True }, "documentHighlight": { "dynamicRegistration": True }, "documentSymbol": { "dynamicRegistration": True }, "codeAction": { "dynamicRegistration": True }, "codeLens": { "dynamicRegistration": True }, "formatting": { "dynamicRegistration": True }, "rangeFormatting": { "dynamicRegistration": True }, "onTypeFormatting": { "dynamicRegistration": True }, "rename": { "dynamicRegistration": True }, "documentLink": { "dynamicRegistration": True }, }, }, "trace": "off", }, }) assert "capabilities" in msg["result"] return msg
def test_libspec_manager_caches(libspec_manager, workspace_dir): from robocode_ls_core import uris from os.path import os from robotframework_ls_tests.fixtures import LIBSPEC_1 from robotframework_ls_tests.fixtures import LIBSPEC_2 from robotframework_ls_tests.fixtures import LIBSPEC_2_A import time from robocode_ls_core.unittest_tools.fixtures import wait_for_test_condition workspace_dir_a = os.path.join(workspace_dir, "workspace_dir_a") os.makedirs(workspace_dir_a) with open(os.path.join(workspace_dir_a, "my.libspec"), "w") as stream: stream.write(LIBSPEC_1) libspec_manager.add_workspace_folder(uris.from_fs_path(workspace_dir_a)) assert libspec_manager.get_library_info("case1_library", create=False) is not None libspec_manager.remove_workspace_folder(uris.from_fs_path(workspace_dir_a)) library_info = libspec_manager.get_library_info("case1_library", create=False) if library_info is not None: raise AssertionError( "Expected: %s to be None after removing %s" % (library_info, uris.from_fs_path(workspace_dir_a))) libspec_manager.add_workspace_folder(uris.from_fs_path(workspace_dir_a)) assert libspec_manager.get_library_info("case1_library", create=False) is not None # Give a timeout so that the next write will have at least 1 second # difference (1s is the minimum for poll to work). time.sleep(1.1) with open(os.path.join(workspace_dir_a, "my2.libspec"), "w") as stream: stream.write(LIBSPEC_2) def check_spec_found(): library_info = libspec_manager.get_library_info("case2_library", create=False) return library_info is not None # Updating is done in a thread. wait_for_test_condition(check_spec_found, sleep=1 / 5.0) library_info = libspec_manager.get_library_info("case2_library", create=False) assert set(x.name for x in library_info.keywords) == set( ["Case 2 Verify Another Model", "Case 2 Verify Model"]) # Give a timeout so that the next write will have at least 1 second # difference (1s is the minimum for poll to work). time.sleep(1) with open(os.path.join(workspace_dir_a, "my2.libspec"), "w") as stream: stream.write(LIBSPEC_2_A) def check_spec_2_a(): library_info = libspec_manager.get_library_info("case2_library", create=False) if library_info: return set(x.name for x in library_info.keywords) == set( ["Case 2 A Verify Another Model", "Case 2 A Verify Model"]) # Updating is done in a thread. wait_for_test_condition(check_spec_2_a, sleep=1 / 5.0)
def capabilities_vscode(ws_root_path): return { "jsonrpc": "2.0", "id": 0, "method": "initialize", "params": { "processId": None, "clientInfo": { "name": "vscode", "version": "1.44.2" }, "rootPath": ws_root_path, "rootUri": uris.from_fs_path(ws_root_path), "workspaceFolders": [{ "uri": uris.from_fs_path(ws_root_path), "name": os.path.basename(ws_root_path), }], "capabilities": { "workspace": { "applyEdit": True, "workspaceEdit": { "documentChanges": True, "resourceOperations": ["create", "rename", "delete"], "failureHandling": "textOnlyTransactional", }, "didChangeConfiguration": { "dynamicRegistration": True }, "didChangeWatchedFiles": { "dynamicRegistration": True }, "symbol": { "dynamicRegistration": True, "symbolKind": { "valueSet": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, ] }, }, "executeCommand": { "dynamicRegistration": True }, "configuration": True, "workspaceFolders": True, }, "textDocument": { "publishDiagnostics": { "relatedInformation": True, "versionSupport": False, "tagSupport": { "valueSet": [1, 2] }, }, "synchronization": { "dynamicRegistration": True, "willSave": True, "willSaveWaitUntil": True, "didSave": True, }, "completion": { "dynamicRegistration": True, "contextSupport": True, "completionItem": { "snippetSupport": True, "commitCharactersSupport": True, "documentationFormat": ["markdown", "plaintext"], "deprecatedSupport": True, "preselectSupport": True, "tagSupport": { "valueSet": [1] }, }, "completionItemKind": { "valueSet": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, ] }, }, "hover": { "dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"], }, "signatureHelp": { "dynamicRegistration": True, "signatureInformation": { "documentationFormat": ["markdown", "plaintext"], "parameterInformation": { "labelOffsetSupport": True }, }, "contextSupport": True, }, "definition": { "dynamicRegistration": True, "linkSupport": True }, "references": { "dynamicRegistration": True }, "documentHighlight": { "dynamicRegistration": True }, "documentSymbol": { "dynamicRegistration": True, "symbolKind": { "valueSet": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, ] }, "hierarchicalDocumentSymbolSupport": True, }, "codeAction": { "dynamicRegistration": True, "isPreferredSupport": True, "codeActionLiteralSupport": { "codeActionKind": { "valueSet": [ "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports", ] } }, }, "codeLens": { "dynamicRegistration": True }, "formatting": { "dynamicRegistration": True }, "rangeFormatting": { "dynamicRegistration": True }, "onTypeFormatting": { "dynamicRegistration": True }, "rename": { "dynamicRegistration": True, "prepareSupport": True }, "documentLink": { "dynamicRegistration": True, "tooltipSupport": True, }, "typeDefinition": { "dynamicRegistration": True, "linkSupport": True, }, "implementation": { "dynamicRegistration": True, "linkSupport": True, }, "colorProvider": { "dynamicRegistration": True }, "foldingRange": { "dynamicRegistration": True, "rangeLimit": 5000, "lineFoldingOnly": True, }, "declaration": { "dynamicRegistration": True, "linkSupport": True }, "selectionRange": { "dynamicRegistration": True }, }, "window": { "workDoneProgress": True }, }, "trace": "off", }, }
def capabilites_jupyter(ws_root_path): return { "jsonrpc": "2.0", "id": 0, "method": "initialize", "params": { "capabilities": { "textDocument": { "hover": { "dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"], }, "synchronization": { "dynamicRegistration": True, "willSave": False, "didSave": True, "willSaveWaitUntil": False, }, "completion": { "dynamicRegistration": True, "completionItem": { "snippetSupport": False, "commitCharactersSupport": True, "documentationFormat": ["markdown", "plaintext"], "deprecatedSupport": False, "preselectSupport": False, }, "contextSupport": False, }, "signatureHelp": { "dynamicRegistration": True, "signatureInformation": { "documentationFormat": ["markdown", "plaintext"] }, }, "declaration": { "dynamicRegistration": True, "linkSupport": True }, "definition": { "dynamicRegistration": True, "linkSupport": True }, "typeDefinition": { "dynamicRegistration": True, "linkSupport": True, }, "implementation": { "dynamicRegistration": True, "linkSupport": True, }, }, "workspace": { "didChangeConfiguration": { "dynamicRegistration": True } }, }, "initializationOptions": None, "processId": None, "rootUri": uris.from_fs_path(ws_root_path), "workspaceFolders": None, }, }
def get_doc(self, root_relative_path, accept_from_file=True): from robocode_ls_core import uris path = os.path.join(self._ws.root_path, root_relative_path) uri = uris.from_fs_path(path) return self.ws.get_document(uri, accept_from_file=accept_from_file)
def set_root(self, relative_path, **kwargs): from robocode_ls_core import uris from robotframework_ls.impl.robot_workspace import RobotWorkspace path = self._cases.get_path(relative_path) self._ws = RobotWorkspace(uris.from_fs_path(path), **kwargs)
def test_win_from_fs_path(path, uri): assert uris.from_fs_path(path) == uri
# distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from robocode_ls_core.workspace import Document from robocode_ls_core.lsp import TextDocumentContentChangeEvent, Position, Range from robocode_ls_core import uris import pytest import os.path DOC = """document for testing """ DOC_URI = uris.from_fs_path(os.path.abspath(__file__)) @pytest.fixture def doc(): return Document(DOC_URI, DOC) 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"