From 0457e14a62cf36aacfce0ce146da6ce6b6c02550 Mon Sep 17 00:00:00 2001 From: Fabio Zadrozny Date: Wed, 24 Nov 2021 14:49:52 -0300 Subject: [PATCH] Refactoring to allow for user libraries to have the libspec generated / show progress of the generation. WIP #163 --- .../src/robocorp_ls_core/basic.py | 5 + .../src/robocorp_ls_core/progress_report.py | 72 ++++++++- .../src/robocorp_ls_core/protocols.py | 9 ++ .../robot/intellij/RobotPreferences.java | 55 +++++++ .../robot/intellij/RobotPreferencesPage.java | 22 +++ .../intellij/RobotProjectPreferences.java | 55 +++++++ .../intellij/RobotProjectPreferencesPage.java | 22 +++ .../.settings/org.python.pydev.yaml | 2 +- robotframework-ls/codegen/codegen_package.py | 5 + robotframework-ls/package.json | 5 + .../robotframework_ls/impl/libspec_manager.py | 100 ++++-------- .../robotframework_ls/impl/libspec_warmup.py | 144 ++++++++++++++++++ .../impl/robot_lsp_constants.py | 1 + .../robotframework_ls_impl.py | 5 + .../robotframework_ls/server_api/client.py | 6 +- .../robotframework_ls/server_api/server.py | 7 +- .../src/robotframework_ls/server_manager.py | 10 +- .../completions/test_auto_import.py | 35 +++++ ..._collect_from_pre_specified_pythonpath.yml | 30 ++++ .../test_filesystem_completions.py | 34 +++++ ..._collect_from_pre_specified_pythonpath.yml | 17 +++ .../tests/robotframework_ls_tests/fixtures.py | 6 +- 22 files changed, 562 insertions(+), 85 deletions(-) create mode 100644 robotframework-ls/src/robotframework_ls/impl/libspec_warmup.py create mode 100644 robotframework-ls/tests/robotframework_ls_tests/completions/test_auto_import/test_collect_from_pre_specified_pythonpath.yml create mode 100644 robotframework-ls/tests/robotframework_ls_tests/completions/test_filesystem_completions/test_collect_from_pre_specified_pythonpath.yml diff --git a/robocorp-python-ls-core/src/robocorp_ls_core/basic.py b/robocorp-python-ls-core/src/robocorp_ls_core/basic.py index 327a84fa55..bcb04d8f9b 100644 --- a/robocorp-python-ls-core/src/robocorp_ls_core/basic.py +++ b/robocorp-python-ls-core/src/robocorp_ls_core/basic.py @@ -441,3 +441,8 @@ def build_subprocess_kwargs(cwd, env, **kwargs) -> dict: kwargs["env"] = env kwargs["startupinfo"] = startupinfo return kwargs + + +def make_unique(lst): + seen = set() + return [x for x in lst if x not in seen and not seen.add(x)] diff --git a/robocorp-python-ls-core/src/robocorp_ls_core/progress_report.py b/robocorp-python-ls-core/src/robocorp_ls_core/progress_report.py index 2b17972750..e0a9378576 100644 --- a/robocorp-python-ls-core/src/robocorp_ls_core/progress_report.py +++ b/robocorp-python-ls-core/src/robocorp_ls_core/progress_report.py @@ -1,15 +1,22 @@ -from functools import partial -import itertools import threading -from robocorp_ls_core.protocols import IEndPoint, IDirCache +from robocorp_ls_core.protocols import IEndPoint, IDirCache, IProgressReporter from contextlib import contextmanager -from typing import Optional +from typing import Optional, Iterator from robocorp_ls_core.robotframework_log import get_logger +from robocorp_ls_core.basic import implements log = get_logger(__name__) -next_id = partial(next, itertools.count(1)) + + +def _next_id(): + # Note: changed to uuid from incremental because multiple processes + # may start the progress and it shouldn't conflict from one to the + # other. + import uuid + + return str(uuid.uuid4()) class _ProgressReporter(object): @@ -33,7 +40,7 @@ def __init__( self._finished = False self._lock = threading.Lock() - self._id = next_id() + self._id = _next_id() self._expected_time = None self._initial_time = time.time() @@ -107,6 +114,7 @@ def _on_recurrent_timeout(self) -> None: self.endpoint.notify("$/customProgress", args) self.timeout_tracker.call_on_timeout(0.5, self._on_recurrent_timeout) + @implements(IProgressReporter.set_additional_info) def set_additional_info(self, additional_info: str) -> None: self._additional_info = additional_info @@ -125,6 +133,11 @@ def finish(self) -> None: if dir_cache: dir_cache.store(self._last_elapsed_time_key, total_elapsed_time) + def __typecheckself__(self) -> None: + from robocorp_ls_core.protocols import check_implements + + _: IProgressReporter = check_implements(self) + _progress_context = threading.local() @@ -145,13 +158,56 @@ def get_current_progress_reporter() -> Optional[_ProgressReporter]: return None +class ProgressWrapperForTotalWork: + """ + Wraps an IProgressReporter to have a quick way to show stes/total steps. + + i.e.: + + with progress_context(...) as progress_reporter: + progress_wrapper = ProgressWrapperForTotalWork(progress_reporter) + + # Schedule many steps and at each point call. + progress_reporter.increment_total_steps() + + # When a step is done, increment steps done. + progress_reporter.increment_step_done() + """ + + def __init__( + self, + progress_reporter: IProgressReporter, + message: str = "%s of %s", + ) -> None: + self.progress_reporter = progress_reporter + self.message = message + self._lock = threading.Lock() + self._total_steps = 0 + self._current_step = 0 + + def increment_total_steps(self): + with self._lock: + self._total_steps += 1 + self._update_message() + + def increment_step_done(self): + with self._lock: + self._current_step += 1 + self._update_message() + + def _update_message(self): + self.progress_reporter.set_additional_info( + self.message % (self._current_step, self._total_steps) + ) + + @contextmanager def progress_context( endpoint: IEndPoint, title: str, dir_cache: Optional[IDirCache], elapsed_time_key=None, -): +) -> Iterator[IProgressReporter]: """ Creates a progress context which submits $/customProgress notifications to the client. @@ -177,7 +233,7 @@ def progress_context( stack.append(progress_reporter) try: - yield + yield progress_reporter finally: del stack[-1] progress_reporter.finish() diff --git a/robocorp-python-ls-core/src/robocorp_ls_core/protocols.py b/robocorp-python-ls-core/src/robocorp_ls_core/protocols.py index 176d826764..9a80d8f12c 100644 --- a/robocorp-python-ls-core/src/robocorp_ls_core/protocols.py +++ b/robocorp-python-ls-core/src/robocorp_ls_core/protocols.py @@ -128,6 +128,15 @@ def consume(self, message: Dict): """ +class IProgressReporter(Protocol): + def set_additional_info(self, additional_info: str) -> None: + """ + The progress reporter shows the title and elapsed time automatically. + + With this API it's possible to add additional info for the user to see. + """ + + class CommunicationDropped(object): def __str__(self): return "CommunicationDropped" diff --git a/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotPreferences.java b/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotPreferences.java index 9ab2a11c28..315a74e0ad 100644 --- a/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotPreferences.java +++ b/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotPreferences.java @@ -25,6 +25,7 @@ class RobotState { public String robotVariables = ""; public String robotPythonpath = ""; public String robotLibrariesLibdocNeedsArgs = ""; + public String robotLibrariesLibdocPreGenerate = ""; public String robotCodeFormatter = ""; public String robotLintRobocopEnabled = ""; public String robotCompletionsSectionHeadersForm = ""; @@ -45,6 +46,7 @@ public class RobotPreferences implements PersistentStateComponent { public static final String ROBOT_VARIABLES = "robot.variables"; public static final String ROBOT_PYTHONPATH = "robot.pythonpath"; public static final String ROBOT_LIBRARIES_LIBDOC_NEEDS_ARGS = "robot.libraries.libdoc.needsArgs"; + public static final String ROBOT_LIBRARIES_LIBDOC_PRE_GENERATE = "robot.libraries.libdoc.preGenerate"; public static final String ROBOT_CODE_FORMATTER = "robot.codeFormatter"; public static final String ROBOT_LINT_ROBOCOP_ENABLED = "robot.lint.robocop.enabled"; public static final String ROBOT_COMPLETIONS_SECTION_HEADERS_FORM = "robot.completions.section_headers.form"; @@ -67,6 +69,7 @@ public RobotState getState() { robotState.robotVariables = getRobotVariables(); robotState.robotPythonpath = getRobotPythonpath(); robotState.robotLibrariesLibdocNeedsArgs = getRobotLibrariesLibdocNeedsArgs(); + robotState.robotLibrariesLibdocPreGenerate = getRobotLibrariesLibdocPreGenerate(); robotState.robotCodeFormatter = getRobotCodeFormatter(); robotState.robotLintRobocopEnabled = getRobotLintRobocopEnabled(); robotState.robotCompletionsSectionHeadersForm = getRobotCompletionsSectionHeadersForm(); @@ -87,6 +90,7 @@ public void loadState(@NotNull RobotState robotState) { setRobotVariables(robotState.robotVariables); setRobotPythonpath(robotState.robotPythonpath); setRobotLibrariesLibdocNeedsArgs(robotState.robotLibrariesLibdocNeedsArgs); + setRobotLibrariesLibdocPreGenerate(robotState.robotLibrariesLibdocPreGenerate); setRobotCodeFormatter(robotState.robotCodeFormatter); setRobotLintRobocopEnabled(robotState.robotLintRobocopEnabled); setRobotCompletionsSectionHeadersForm(robotState.robotCompletionsSectionHeadersForm); @@ -163,6 +167,14 @@ public JsonObject asJsonObject() { } } + if(!robotLibrariesLibdocPreGenerate.isEmpty()){ + try { + jsonObject.add(ROBOT_LIBRARIES_LIBDOC_PRE_GENERATE, g.fromJson(robotLibrariesLibdocPreGenerate, JsonArray.class)); + } catch(Exception e) { + LOG.error(e); + } + } + if(!robotCodeFormatter.isEmpty()){ try { jsonObject.add(ROBOT_CODE_FORMATTER, new JsonPrimitive(robotCodeFormatter)); @@ -551,6 +563,49 @@ public void setRobotLibrariesLibdocNeedsArgs(String s) { } } + private String robotLibrariesLibdocPreGenerate = ""; + + public @NotNull String getRobotLibrariesLibdocPreGenerate() { + return robotLibrariesLibdocPreGenerate; + } + + public @Nullable JsonArray getRobotLibrariesLibdocPreGenerateAsJson() { + if(robotLibrariesLibdocPreGenerate.isEmpty()){ + return null; + } + Gson g = new Gson(); + return g.fromJson(robotLibrariesLibdocPreGenerate, JsonArray.class); + } + + public @NotNull String validateRobotLibrariesLibdocPreGenerate(String robotLibrariesLibdocPreGenerate) { + if(robotLibrariesLibdocPreGenerate.isEmpty()) { + return ""; + } + try { + Gson g = new Gson(); + g.fromJson(robotLibrariesLibdocPreGenerate, JsonArray.class); + + return ""; + + } catch(Exception e) { + return e.toString(); + } + } + + public void setRobotLibrariesLibdocPreGenerate(String s) { + if (s == null) { + s = ""; + } + if (s.equals(robotLibrariesLibdocPreGenerate)) { + return; + } + String old = robotLibrariesLibdocPreGenerate; + robotLibrariesLibdocPreGenerate = s; + for (LanguageServerDefinition.IPreferencesListener listener : listeners) { + listener.onChanged(ROBOT_LIBRARIES_LIBDOC_PRE_GENERATE, old, s); + } + } + private String robotCodeFormatter = ""; public @NotNull String getRobotCodeFormatter() { diff --git a/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotPreferencesPage.java b/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotPreferencesPage.java index 222bfa3881..3ce8d18bf2 100644 --- a/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotPreferencesPage.java +++ b/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotPreferencesPage.java @@ -28,6 +28,7 @@ class RobotPreferencesComponent { private final JBTextField robotVariables = new JBTextField(); private final JBTextField robotPythonpath = new JBTextField(); private final JBTextField robotLibrariesLibdocNeedsArgs = new JBTextField(); + private final JBTextField robotLibrariesLibdocPreGenerate = new JBTextField(); private final JBTextField robotCodeFormatter = new JBTextField(); private final JBTextField robotLintRobocopEnabled = new JBTextField(); private final JBTextField robotCompletionsSectionHeadersForm = new JBTextField(); @@ -52,6 +53,8 @@ public RobotPreferencesComponent() { .addComponent(createJTextArea("Entries to be added to the PYTHONPATH\n(used when resolving resources and imports and automatically passed to the launch config as\n--pythonpath entries).\n(i.e.: [\"c:/my/pro/src\"])\nNote: expected format: JSON Array\n")) .addLabeledComponent(new JBLabel("Libraries Libdoc Needs Args"), robotLibrariesLibdocNeedsArgs, 1, false) .addComponent(createJTextArea("Libraries which will generate a different set of keywords based on the arguments provided.\n(i.e.: [\"remote\", \"fakerlib\"])\nNote: expected format: JSON Array\n")) + .addLabeledComponent(new JBLabel("Libraries Libdoc Pre Generate"), robotLibrariesLibdocPreGenerate, 1, false) + .addComponent(createJTextArea("List of libraries which should have the libspec pre-generated.\nNote: expected format: JSON Array\n")) .addLabeledComponent(new JBLabel("Code Formatter"), robotCodeFormatter, 1, false) .addComponent(createJTextArea("Allows the configuration of the code-formatter engine to be used. One of: robotidy, builtinTidy.\n")) .addLabeledComponent(new JBLabel("Lint Robocop Enabled"), robotLintRobocopEnabled, 1, false) @@ -158,6 +161,15 @@ public void setRobotLibrariesLibdocNeedsArgs (@NotNull String newText) { robotLibrariesLibdocNeedsArgs.setText(newText); } + @NotNull + public String getRobotLibrariesLibdocPreGenerate() { + return robotLibrariesLibdocPreGenerate.getText(); + } + + public void setRobotLibrariesLibdocPreGenerate (@NotNull String newText) { + robotLibrariesLibdocPreGenerate.setText(newText); + } + @NotNull public String getRobotCodeFormatter() { return robotCodeFormatter.getText(); @@ -263,6 +275,10 @@ public boolean isModified() { return true; } + if(!settings.getRobotLibrariesLibdocPreGenerate().equals(component.getRobotLibrariesLibdocPreGenerate())){ + return true; + } + if(!settings.getRobotCodeFormatter().equals(component.getRobotCodeFormatter())){ return true; } @@ -298,6 +314,7 @@ public void reset() { component.setRobotVariables(settings.getRobotVariables()); component.setRobotPythonpath(settings.getRobotPythonpath()); component.setRobotLibrariesLibdocNeedsArgs(settings.getRobotLibrariesLibdocNeedsArgs()); + component.setRobotLibrariesLibdocPreGenerate(settings.getRobotLibrariesLibdocPreGenerate()); component.setRobotCodeFormatter(settings.getRobotCodeFormatter()); component.setRobotLintRobocopEnabled(settings.getRobotLintRobocopEnabled()); component.setRobotCompletionsSectionHeadersForm(settings.getRobotCompletionsSectionHeadersForm()); @@ -342,6 +359,10 @@ public void apply() throws ConfigurationException { if(!s.isEmpty()) { throw new ConfigurationException("Error in Libraries Libdoc Needs Args:\n" + s); } + s = settings.validateRobotLibrariesLibdocPreGenerate(component.getRobotLibrariesLibdocPreGenerate()); + if(!s.isEmpty()) { + throw new ConfigurationException("Error in Libraries Libdoc Pre Generate:\n" + s); + } s = settings.validateRobotCodeFormatter(component.getRobotCodeFormatter()); if(!s.isEmpty()) { throw new ConfigurationException("Error in Code Formatter:\n" + s); @@ -371,6 +392,7 @@ public void apply() throws ConfigurationException { settings.setRobotVariables(component.getRobotVariables()); settings.setRobotPythonpath(component.getRobotPythonpath()); settings.setRobotLibrariesLibdocNeedsArgs(component.getRobotLibrariesLibdocNeedsArgs()); + settings.setRobotLibrariesLibdocPreGenerate(component.getRobotLibrariesLibdocPreGenerate()); settings.setRobotCodeFormatter(component.getRobotCodeFormatter()); settings.setRobotLintRobocopEnabled(component.getRobotLintRobocopEnabled()); settings.setRobotCompletionsSectionHeadersForm(component.getRobotCompletionsSectionHeadersForm()); diff --git a/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotProjectPreferences.java b/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotProjectPreferences.java index c0ab88a530..189c009c75 100644 --- a/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotProjectPreferences.java +++ b/robotframework-intellij/src/main/java/robocorp/robot/intellij/RobotProjectPreferences.java @@ -25,6 +25,7 @@ class RobotProjectState { public String robotVariables = ""; public String robotPythonpath = ""; public String robotLibrariesLibdocNeedsArgs = ""; + public String robotLibrariesLibdocPreGenerate = ""; public String robotCodeFormatter = ""; public String robotLintRobocopEnabled = ""; public String robotCompletionsSectionHeadersForm = ""; @@ -45,6 +46,7 @@ public class RobotProjectPreferences implements PersistentStateComponent None: + """ + Generates .libspec files for the libraries builtin (if needed). + """ + from robotframework_ls.impl import robot_constants + from robotframework_ls.impl.robot_constants import RESERVED_LIB + + def provide_args_and_kwargs_to_create_libspec(): + for libname in robot_constants.STDLIBS: + if libname == RESERVED_LIB: + continue + builtins_libspec_dir = libspec_manager._builtins_libspec_dir + if not os.path.exists( + os.path.join(builtins_libspec_dir, f"{libname}.libspec") + ): + yield (libname,), dict(is_builtin=True) + + self._generate( + libspec_manager, + progress_title="Generate .libspec for builtin libraries", + elapsed_time_key="generate_builtins_libspec", + mutex_name_prefix="gen_builtins_", + on_finish=lambda: None, + provide_args_and_kwargs_to_create_libspec=provide_args_and_kwargs_to_create_libspec, + ) + + def gen_user_libraries(self, libspec_manager, user_libraries: List[str]): + for name in user_libraries: + libspec_manager._create_libspec(name) + libspec_manager.synchronize_internal_libspec_folders() diff --git a/robotframework-ls/src/robotframework_ls/impl/robot_lsp_constants.py b/robotframework-ls/src/robotframework_ls/impl/robot_lsp_constants.py index 1ac0778774..2db801e65c 100644 --- a/robotframework-ls/src/robotframework_ls/impl/robot_lsp_constants.py +++ b/robotframework-ls/src/robotframework_ls/impl/robot_lsp_constants.py @@ -24,6 +24,7 @@ OPTION_ROBOT_PYTHONPATH = "robot.pythonpath" OPTION_ROBOT_LIBRARIES_LIBDOC_NEEDS_ARGS = "robot.libraries.libdoc.needsArgs" +OPTION_ROBOT_LIBRARIES_LIBDOC_PRE_GENERATE = "robot.libraries.libdoc.preGenerate" OPTION_ROBOT_WORKSPACE_SYMBOLS_ONLY_FOR_OPEN_DOCS = ( "robot.workspaceSymbolsOnlyForOpenDocs" diff --git a/robotframework-ls/src/robotframework_ls/robotframework_ls_impl.py b/robotframework-ls/src/robotframework_ls/robotframework_ls_impl.py index e091e8c6dd..a74d79310e 100644 --- a/robotframework-ls/src/robotframework_ls/robotframework_ls_impl.py +++ b/robotframework-ls/src/robotframework_ls/robotframework_ls_impl.py @@ -267,6 +267,11 @@ def m_initialize( return ret + def forward_progress_msg(self, msg: dict) -> None: + method = msg["method"] + assert method == "$/customProgress" + self._endpoint.notify(method, msg["params"]) + @overrides(PythonLanguageServer.capabilities) def capabilities(self): from robocorp_ls_core.lsp import TextDocumentSyncKind diff --git a/robotframework-ls/src/robotframework_ls/server_api/client.py b/robotframework-ls/src/robotframework_ls/server_api/client.py index c2a1f5e568..5551e22f3b 100644 --- a/robotframework-ls/src/robotframework_ls/server_api/client.py +++ b/robotframework-ls/src/robotframework_ls/server_api/client.py @@ -16,8 +16,10 @@ class SubprocessDiedError(Exception): class RobotFrameworkApiClient(LanguageServerClientBase): - def __init__(self, writer, reader, server_process): - LanguageServerClientBase.__init__(self, writer, reader) + def __init__(self, writer, reader, server_process, on_received_message=None): + LanguageServerClientBase.__init__( + self, writer, reader, on_received_message=on_received_message + ) self.server_process = server_process self._check_process_alive() self._version = None diff --git a/robotframework-ls/src/robotframework_ls/server_api/server.py b/robotframework-ls/src/robotframework_ls/server_api/server.py index cbbaddf136..c406dbb989 100644 --- a/robotframework-ls/src/robotframework_ls/server_api/server.py +++ b/robotframework-ls/src/robotframework_ls/server_api/server.py @@ -38,15 +38,18 @@ def __init__( ): from robotframework_ls.impl.libspec_manager import LibspecManager + PythonLanguageServer.__init__(self, read_from, write_to) + if libspec_manager is None: try: - libspec_manager = LibspecManager(observer=observer) + libspec_manager = LibspecManager( + observer=observer, endpoint=self._endpoint + ) except: log.exception("Unable to properly initialize the LibspecManager.") raise self.libspec_manager = libspec_manager - PythonLanguageServer.__init__(self, read_from, write_to) self._version = None self._next_time = partial(next, itertools.count(0)) diff --git a/robotframework-ls/src/robotframework_ls/server_manager.py b/robotframework-ls/src/robotframework_ls/server_manager.py index 4a0111ce8f..20bccc51c4 100644 --- a/robotframework-ls/src/robotframework_ls/server_manager.py +++ b/robotframework-ls/src/robotframework_ls/server_manager.py @@ -251,8 +251,16 @@ def get_robotframework_api_client(self) -> Optional[IRobotFrameworkApiClient]: w = JsonRpcStreamWriter(write_to, sort_keys=True) r = JsonRpcStreamReader(read_from) + language_server_ref = self._language_server_ref + + def on_received_message(msg): + if msg.get("method") == "$/customProgress": + robot_framework_language_server = language_server_ref() + if robot_framework_language_server is not None: + robot_framework_language_server.forward_progress_msg(msg) + api = self._robotframework_api_client = RobotFrameworkApiClient( - w, r, server_process + w, r, server_process, on_received_message=on_received_message ) log.debug( diff --git a/robotframework-ls/tests/robotframework_ls_tests/completions/test_auto_import.py b/robotframework-ls/tests/robotframework_ls_tests/completions/test_auto_import.py index de42a8b145..4138a2b2a2 100644 --- a/robotframework-ls/tests/robotframework_ls_tests/completions/test_auto_import.py +++ b/robotframework-ls/tests/robotframework_ls_tests/completions/test_auto_import.py @@ -557,3 +557,38 @@ def test_no_reserved_keywords(workspace, setup_case2_in_dir_doc): ) for completion in completions: assert completion["label"] != "Else If (Reserved)" + + +def test_collect_from_pre_specified_pythonpath( + workspace, cases, libspec_manager, data_regression +): + from robotframework_ls.impl.completion_context import CompletionContext + from robotframework_ls.impl import auto_import_completions + from robotframework_ls.robot_config import RobotConfig + + workspace.set_root("case1", libspec_manager=libspec_manager) + + pythonpath = [cases.get_path("case1"), cases.get_path("case_search_pythonpath")] + config = RobotConfig() + config.update( + { + "robot": { + "pythonpath": pythonpath, + "libraries": { + "libdoc": {"preGenerate": ["libraries.lib_in_pythonpath"]} + }, + } + } + ) + libspec_manager.config = config + doc = workspace.get_doc("case1.robot") + + doc.source = """ +*** Keywords *** +KeywordInCase1 + Find in lib""" + + completions = auto_import_completions.complete( + CompletionContext(doc, workspace=workspace.ws), {} + ) + data_regression.check(completions) diff --git a/robotframework-ls/tests/robotframework_ls_tests/completions/test_auto_import/test_collect_from_pre_specified_pythonpath.yml b/robotframework-ls/tests/robotframework_ls_tests/completions/test_auto_import/test_collect_from_pre_specified_pythonpath.yml new file mode 100644 index 0000000000..2447d4a341 --- /dev/null +++ b/robotframework-ls/tests/robotframework_ls_tests/completions/test_auto_import/test_collect_from_pre_specified_pythonpath.yml @@ -0,0 +1,30 @@ +- additionalTextEdits: + - newText: '*** Settings *** + + Library libraries.lib_in_pythonpath + + ' + range: + end: + character: 0 + line: 0 + start: + character: 0 + line: 0 + deprecated: false + documentation: '' + documentationFormat: plaintext + insertText: Find In Library + insertTextFormat: 2 + kind: 18 + label: Find In Library (libraries.lib_in_pythonpath) + preselect: false + textEdit: + newText: Find In Library + range: + end: + character: 15 + line: 3 + start: + character: 4 + line: 3 diff --git a/robotframework-ls/tests/robotframework_ls_tests/completions/test_filesystem_completions.py b/robotframework-ls/tests/robotframework_ls_tests/completions/test_filesystem_completions.py index 43f87ce943..237c080858 100644 --- a/robotframework-ls/tests/robotframework_ls_tests/completions/test_filesystem_completions.py +++ b/robotframework-ls/tests/robotframework_ls_tests/completions/test_filesystem_completions.py @@ -174,3 +174,37 @@ def test_resource_completions_resolve_var( ) data_regression.check(completions) + + +def test_collect_from_pre_specified_pythonpath( + workspace, cases, libspec_manager, data_regression +): + from robotframework_ls.impl.completion_context import CompletionContext + from robotframework_ls.impl import filesystem_section_completions + from robotframework_ls.robot_config import RobotConfig + + workspace.set_root("case1", libspec_manager=libspec_manager) + + pythonpath = [cases.get_path("case1"), cases.get_path("case_search_pythonpath")] + config = RobotConfig() + config.update( + { + "robot": { + "pythonpath": pythonpath, + "libraries": { + "libdoc": {"preGenerate": ["libraries.lib_in_pythonpath"]} + }, + } + } + ) + libspec_manager.config = config + doc = workspace.get_doc("case1.robot") + + doc.source = """ +*** Settings *** +Library librari""" + + completions = filesystem_section_completions.complete( + CompletionContext(doc, workspace=workspace.ws, config=config) + ) + data_regression.check(completions) diff --git a/robotframework-ls/tests/robotframework_ls_tests/completions/test_filesystem_completions/test_collect_from_pre_specified_pythonpath.yml b/robotframework-ls/tests/robotframework_ls_tests/completions/test_filesystem_completions/test_collect_from_pre_specified_pythonpath.yml new file mode 100644 index 0000000000..b78bd11744 --- /dev/null +++ b/robotframework-ls/tests/robotframework_ls_tests/completions/test_filesystem_completions/test_collect_from_pre_specified_pythonpath.yml @@ -0,0 +1,17 @@ +- deprecated: false + documentation: '' + documentationFormat: plaintext + insertText: libraries.lib_in_pythonpath + insertTextFormat: 2 + kind: 9 + label: libraries.lib_in_pythonpath + preselect: false + textEdit: + newText: libraries.lib_in_pythonpath + range: + end: + character: 18 + line: 2 + start: + character: 11 + line: 2 diff --git a/robotframework-ls/tests/robotframework_ls_tests/fixtures.py b/robotframework-ls/tests/robotframework_ls_tests/fixtures.py index 0d352c135c..e3c39bbe4b 100644 --- a/robotframework-ls/tests/robotframework_ls_tests/fixtures.py +++ b/robotframework-ls/tests/robotframework_ls_tests/fixtures.py @@ -192,7 +192,7 @@ def write_on_finish(): remote_fsobserver.dispose() on_timeout.remove(write_on_finish) - write_on_finish() + # write_on_finish() -- usually the remote fs observer isn't core to tests, so, don't print by default. @pytest.fixture @@ -204,7 +204,9 @@ def libspec_manager(tmpdir, remote_fs_observer): from robotframework_ls.impl.libspec_manager import LibspecManager libspec_manager = LibspecManager( - user_libspec_dir=str(tmpdir.join("user_libspec")), observer=remote_fs_observer + user_libspec_dir=str(tmpdir.join("user_libspec")), + observer=remote_fs_observer, + dir_cache_dir=str(tmpdir.join(".cache")), ) yield libspec_manager libspec_manager.dispose()