def __init__( self, root_uri: str, workspace_folders: Optional[List[IWorkspaceFolder]] = None, track_file_extensions=(".robot", ".resource"), ) -> None: from robocorp_ls_core.lsp import WorkspaceFolder self._main_thread = threading.current_thread() self._root_uri = root_uri self._root_uri_scheme = uri_scheme(self._root_uri) self._root_path = to_fs_path(self._root_uri) self._folders: Dict[str, _WorkspaceFolderWithVirtualFS] = {} self._track_file_extensions = track_file_extensions # Contains the docs with files considered open. self._docs: Dict[str, IDocument] = {} # Contains the docs pointing to the filesystem. self._filesystem_docs: Dict[str, IDocument] = {} if workspace_folders is not None: for folder in workspace_folders: self.add_folder(folder) if root_uri and root_uri not in self._folders: as_fs_path = uris.to_fs_path(root_uri) name = os.path.basename(as_fs_path) self.add_folder(WorkspaceFolder(root_uri, name))
def _do_create_libspec_on_get(self, libname, current_doc_uri, arguments, alias): from robocorp_ls_core import uris additional_path = None abspath = None cwd = None if current_doc_uri is not None: cwd = os.path.dirname(uris.to_fs_path(current_doc_uri)) if not cwd or not os.path.isdir(cwd): cwd = None if os.path.isabs(libname): abspath = libname elif current_doc_uri is not None: # relative path: let's make it absolute fs_path = os.path.dirname(uris.to_fs_path(current_doc_uri)) abspath = os.path.abspath(os.path.join(fs_path, libname)) if abspath: additional_path = os.path.dirname(abspath) libname = os.path.basename(libname) if libname.lower().endswith((".py", ".class", ".java")): libname = os.path.splitext(libname)[0] if self._create_libspec(libname, additional_path=additional_path, cwd=cwd, arguments=arguments, alias=alias, current_doc_uri=current_doc_uri): self.synchronize_internal_libspec_folders() return True return False
def get_library_target_filename( self, libname: str, current_doc_uri: Optional[str] = None) -> Optional[str]: from robocorp_ls_core import uris target_file: Optional[str] = None libname_lower = libname.lower() if os.path.isabs(libname): target_file = libname else: # Check if it maps to a file in the filesystem if current_doc_uri is not None: cwd = os.path.dirname(uris.to_fs_path(current_doc_uri)) if cwd and os.path.isdir(cwd): f = os.path.join(cwd, libname) if os.path.exists(f): target_file = f elif not libname_lower.endswith(".py"): f += ".py" if os.path.exists(f): target_file = f return target_file
def __init__( self, verbose: int = 0, base_log_file: str = "", on_interpreter_message=None, uri: str = "", ): from robotframework_interactive.server.rf_interpreter_ls_config import ( RfInterpreterRobotConfig, ) from robocorp_ls_core import uris assert uri self._uri = uri self._filename = uris.to_fs_path(uri) self._lock_api_client = threading.RLock() self._server_process = None self._log_extension = ".rf_interpreter" self._disposed = False # The config allows clients to set the python executable/env. self._config: IConfig = RfInterpreterRobotConfig() self._verbose = verbose self._base_log_file = base_log_file self._on_interpreter_message = on_interpreter_message
def m_text_document__hover(self, **kwargs): """ When hovering over a png in base64 surrounded by double-quotes... something as: "iVBORw0KGgo...rest of png in base 64 contents..." i.e.: Provide the contents in markdown format to show the actual image from the locators.json. """ from robocorp_ls_core import uris from robocorp_ls_core.protocols import IDocument from robocorp_ls_core.protocols import IDocumentSelection from robocorp_ls_core.lsp import Range from robocorp_ls_core.lsp import MarkupKind from robocorp_ls_core.lsp import MarkupContent doc_uri = kwargs["textDocument"]["uri"] # Note: 0-based line: int = kwargs["position"]["line"] col: int = kwargs["position"]["character"] if not uris.to_fs_path(doc_uri).endswith("locators.json"): return None document: IDocument = self._workspace.get_document( doc_uri, accept_from_file=True) sel: IDocumentSelection = document.selection(line, col) current_line: str = sel.current_line i: int = current_line.find( '"iVBORw0KGgo' ) # I.e.: pngs in base64 always start with this prefix. if i >= 0: current_line = current_line[i + 1:] i = current_line.find('"') if i >= 0: current_line = current_line[0:i] image_path = f"data:image/png;base64,{current_line}" s = f"![Screenshot]({image_path})" return { "contents": MarkupContent(MarkupKind.Markdown, s).to_dict(), "range": Range((line, col), (line, col)).to_dict(), } # Could not find a base-64 img embedded, let's see if we have an element # with a relative path. import re p = Path(document.path).parent for found in re.findall('"(.+?)"', current_line): if found.endswith(".png"): check = p / found if check.exists(): as_uri = uris.from_fs_path(str(check)) s = f"![Screenshot]({as_uri})" return { "contents": MarkupContent(MarkupKind.Markdown, s).to_dict(), "range": Range((line, col), (line, col)).to_dict(), } return None
def __init__(self, uri, name, track_file_extensions, fs_observer: IFSObserver): self.uri = uri self.name = name self.path = uris.to_fs_path(uri) self._vs: _VirtualFS = _VirtualFS( self.path, track_file_extensions, fs_observer=fs_observer )
def get_interpreter_info_for_doc_uri(self, doc_uri) -> Optional[IInterpreterInfo]: try: pm = self._pm() if pm is None: log.critical("Did not expect PluginManager to be None at this point.") return None from robotframework_ls.ep_providers import ( EPConfigurationProvider, EPEndPointProvider, ) # Check that our requirements are there. pm[EPConfigurationProvider] pm[EPEndPointProvider] fs_path = Path(uris.to_fs_path(doc_uri)) for path in fs_path.parents: robot_yaml: Path = path / "robot.yaml" if robot_yaml.exists(): break else: # i.e.: Could not find any robot.yaml in the structure. log.debug("Could not find robot.yaml for: %s", fs_path) return None # Ok, we have the robot_yaml, so, we should be able to run RCC with it. robot_yaml_file_info = _CacheInfo.get_file_info(robot_yaml) yaml_contents = robot_yaml_file_info.yaml_contents if not isinstance(yaml_contents, dict): raise AssertionError(f"Expected dict as root in: {robot_yaml}") conda_config = yaml_contents.get("condaConfigFile") conda_config_file_info = None env_json_path_file_info = None if conda_config: parent: Path = robot_yaml.parent conda_config_path = parent / conda_config if conda_config_path.exists(): conda_config_file_info = _CacheInfo.get_file_info(conda_config_path) env_json_path = parent / "devdata" / "env.json" if env_json_path.exists(): env_json_path_file_info = _CacheInfo.get_file_info(env_json_path) return _CacheInfo.get_interpreter_info( robot_yaml_file_info, conda_config_file_info, env_json_path_file_info, pm, ) except: log.exception(f"Error getting interpreter info for: {doc_uri}") return None
def __init__(self, uri: str, source=None, version: Optional[str] = None): self._main_thread = threading.current_thread() self.uri = uri self.version = version self.path = uris.to_fs_path(uri) # Note: may be None. self._source = source self.__line_start_offsets = None # Only set when the source is read from disk. self._source_mtime = -1
def __init__(self, resource_doc): """ :param RobotDocument resource_doc: """ from robocorp_ls_core import uris self.keyword_name = "" self.resource_doc = resource_doc self.source = uris.to_fs_path(resource_doc.uri) self.lineno = 1 self.end_lineno = 1 self.col_offset = 1 self.end_col_offset = 1
def __init__(self, variables_doc): """ :param RobotDocument variables_doc: """ from robocorp_ls_core import uris self.keyword_name = "" self.variables_doc = variables_doc self.source = uris.to_fs_path(variables_doc.uri) # Note: line/offsets 0-based. self.lineno = 0 self.end_lineno = 0 self.col_offset = 0 self.end_col_offset = 0
def add_workspace_folder(self, folder_uri: str): self._check_in_main_thread() from robocorp_ls_core import uris if folder_uri not in self._workspace_folder_uri_to_folder_info: log.debug("Added workspace folder: %s", folder_uri) cp = self._workspace_folder_uri_to_folder_info.copy() folder_info = cp[folder_uri] = _FolderInfo( uris.to_fs_path(folder_uri), recursive=True ) self._workspace_folder_uri_to_folder_info = cp folder_info.start_watch(self._observer, self._file_changes_notifier) folder_info.synchronize() else: log.debug("Workspace folder already added: %s", folder_uri)
def __init__(self, root_uri, workspace_folders=None) -> None: from robocorp_ls_core.lsp import WorkspaceFolder self._main_thread = threading.current_thread() self._root_uri = root_uri self._root_uri_scheme = uri_scheme(self._root_uri) self._root_path = to_fs_path(self._root_uri) self._folders: Dict[str, WorkspaceFolder] = {} # Contains the docs with files considered open. self._docs: Dict[str, IDocument] = {} # Contains the docs pointing to the filesystem. self._filesystem_docs: Dict[str, IDocument] = {} if workspace_folders is not None: for folder in workspace_folders: self.add_folder(folder) if root_uri and root_uri not in self._folders: as_fs_path = uris.to_fs_path(root_uri) name = os.path.basename(as_fs_path) self.add_folder(WorkspaceFolder(root_uri, name))
def get_resource_import_as_doc(self, resource_import) -> Optional[IRobotDocument]: from robocorp_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( os.path.abspath( os.path.join(os.path.normpath(uris.to_fs_path(ws.root_uri)), additional_pythonpath_entry)) if ws.root_uri is not None and not os.path.isabs(additional_pythonpath_entry) else 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 _add_completions_from_dir( completion_context, directory, matcher, ret, sel, token, qualifier, extensions, skip_current, ): from robocorp_ls_core import uris def normfile(path): return os.path.normpath(os.path.normcase(path)) curr_file = normfile(uris.to_fs_path(completion_context.doc.uri)) try: # This is ok if the directory doesn't exist. contents = sorted(os.listdir(directory)) except: return for filename in contents: use_path = None if filename.endswith(extensions): # If that'd be a match for the current .robot file, don't show it. if skip_current and curr_file == normfile( os.path.join(directory, filename)): continue use_path = filename elif filename not in ("__pycache__", ".git") and os.path.isdir( os.path.join(directory, filename)): use_path = filename + "/" else: continue if matcher.accepts(use_path): ret.append( _create_completion_item(use_path, sel, token, start_col_offset=sel.col - len(qualifier)))
def get_interpreter_info_for_doc_uri(self, doc_uri) -> Optional[IInterpreterInfo]: as_path = Path(uris.to_fs_path(doc_uri)) environ = dict(os.environ) if as_path.parent.name == "env1": return DefaultInterpreterInfo( "env1", sys.executable, environ, [str(as_path.parent / "lib1")] ) elif as_path.parent.name == "env2": return DefaultInterpreterInfo( "env2", sys.executable, environ, [str(as_path.parent / "lib2")] ) return None
def _get_library_target_filename( self, libname: str, current_doc_uri: Optional[str] = None) -> Optional[str]: from robocorp_ls_core import uris target_file: Optional[str] = None libname_lower = libname.lower() if os.path.isabs(libname): target_file = libname else: # Check if it maps to a file in the filesystem if current_doc_uri is not None: cwd = os.path.dirname(uris.to_fs_path(current_doc_uri)) if cwd and os.path.isdir(cwd): f = os.path.join(cwd, libname) if os.path.isdir(f): f = os.path.join(f, "__init__.py") if os.path.exists(f): target_file = f elif not libname_lower.endswith(".py"): f += ".py" if os.path.exists(f): target_file = f if target_file is None and libname.endswith( (".py", ".class", ".java")): iter_in_pythonpath_directories = itertools.chain( self._additional_pythonpath_folder_to_folder_info.keys(), self._pythonpath_folder_to_folder_info.keys(), ) # https://github.com/robocorp/robotframework-lsp/issues/266 # If the user specifies a file, we don't just search the current # relative folder, we also need to search for relative entries # in the whole PYTHONPATH. for entry in iter_in_pythonpath_directories: check_target = os.path.join(entry, libname) if os.path.exists(check_target): target_file = check_target break return target_file
def test_keyword_completions_user_library( data_regression, workspace, cases, libspec_manager, library_import, workspace_dir ): import os.path from robotframework_ls.impl import keyword_completions from robotframework_ls.impl.completion_context import CompletionContext from robotframework_ls_tests.fixtures import LIBSPEC_1 from robocorp_ls_core import uris cases.copy_to("case1", workspace_dir) workspace.set_root(workspace_dir, libspec_manager=libspec_manager) doc = workspace.get_doc("case1.robot") if library_import == "__FULL_PATH__": case1_robot_path = uris.to_fs_path(doc.uri) case1_py_path = os.path.join( os.path.dirname(case1_robot_path), "case1_library.py" ) library_import = case1_py_path doc.source = doc.source.replace(u"case1_library", library_import) doc.source = doc.source + u"\n verify" completions = keyword_completions.complete( CompletionContext(doc, workspace=workspace.ws) ) data_regression.check(completions, basename="keyword_completions_1") # Now, let's put a .libspec file in the workspace and check whether # it has priority over the auto-generated spec file. with open(os.path.join(workspace_dir, "new_spec.libspec"), "w") as stream: stream.write(LIBSPEC_1) libspec_manager.synchronize_workspace_folders() completions = keyword_completions.complete( CompletionContext(doc, workspace=workspace.ws) ) data_regression.check(completions, basename="keyword_completions_2_new")
def test_server_stdin(setup: _Setup): from robocorp_ls_core import uris import os received_messages = setup.received_messages rf_interpreter_server_manager = setup.rf_interpreter_server_manager result = rf_interpreter_server_manager.interpreter_start(setup.uri) assert result["success"], f"Found: {result}" uri = setup.uri robot_file = uris.to_fs_path(uri) lib_file = os.path.join(os.path.dirname(robot_file), "my_lib.py") with open(lib_file, "w", encoding="utf-8") as stream: stream.write(r""" def check_input(): import sys sys.__stdout__.write('Enter something\n') return input() """) rf_interpreter_server_manager.interpreter_evaluate( "*** Settings ***\nLibrary ./my_lib.py") def check_input_in_thread(): rf_interpreter_server_manager.interpreter_evaluate("Check Input") threading.Thread(target=check_input_in_thread).start() def wait_for_enter_something_output(): for msg in received_messages: if (msg["method"] == "interpreter/output" and "Enter something" in msg["params"]["output"]): return True return False wait_for_condition(wait_for_enter_something_output) assert rf_interpreter_server_manager._get_api_client().waiting_input rf_interpreter_server_manager.interpreter_evaluate("Something\n")
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)
def add_additional_pythonpath_folder(self, folder_path): self._check_in_main_thread() from robocorp_ls_core import uris if folder_path not in self._additional_pythonpath_folder_to_folder_info: log.debug("Added additional pythonpath folder: %s", folder_path) cp = self._additional_pythonpath_folder_to_folder_info.copy() real_path = os.path.abspath( os.path.join(os.path.normpath(uris.to_fs_path(self.root_uri)), folder_path) ) if self.root_uri is not None and not os.path.isabs( folder_path) else folder_path folder_info = cp[folder_path] = _FolderInfo(real_path, recursive=True) self._additional_pythonpath_folder_to_folder_info = cp folder_info.start_watch(self._observer, self._file_changes_notifier) folder_info.synchronize() else: log.debug("Additional pythonpath folder already added: %s", folder_path)
def _collect_auto_import_completions(completion_context: ICompletionContext, collector: _Collector): from robotframework_ls.impl.workspace_symbols import iter_symbols_caches from robotframework_ls.impl.protocols import ISymbolsCache from robocorp_ls_core.protocols import IWorkspace from robotframework_ls.robot_config import create_convert_keyword_format_func symbols_cache: ISymbolsCache selection = completion_context.sel token = collector.token ws: IWorkspace = completion_context.workspace folder_paths = [] for folder in ws.iter_folders(): folder_paths.append(uris.to_fs_path(folder.uri)) curr_doc_path = os.path.dirname(uris.to_fs_path( completion_context.doc.uri)) memo: Set[str] = set() default_convert_keyword_format = create_convert_keyword_format_func( completion_context.config) noop = lambda x: x for symbols_cache in iter_symbols_caches(None, completion_context, show_builtins=False): library_info: Optional[ILibraryDoc] = symbols_cache.get_library_info() doc: Optional[IRobotDocument] = symbols_cache.get_doc() lib_import = None resource_path = None if library_info is not None: if library_info.source: if (library_info.source in collector.import_location_info.imported_libraries): continue elif library_info.name in collector.import_location_info.imported_libraries: continue if library_info.source: for folder_path in folder_paths: # If the library is found to be in the workspace, use a relative # path, otherwise use the library name (in which case it's expected # to be in the pythonpath). if library_info.source.startswith(folder_path): try: lib_import = os.path.relpath( library_info.source, curr_doc_path).replace("\\", "/") break except: pass else: lib_import = library_info.name else: lib_import = library_info.name convert_keyword_format = default_convert_keyword_format elif doc is not None: resource_path = doc.path try: resource_path = os.path.relpath(resource_path, curr_doc_path).replace( "\\", "/") except: pass convert_keyword_format = noop json_list = symbols_cache.get_json_list() for entry in json_list: if collector.accepts(entry): collector.create_completion_item( completion_context, convert_keyword_format(entry["name"]), selection, token, 0, memo, lib_import=lib_import, resource_path=resource_path, data=None, )
def _threaded_lint(self, doc_uri, monitor: IMonitor): from robocorp_ls_core.jsonrpc.exceptions import JsonRpcRequestCancelled from robotframework_ls.impl.robot_lsp_constants import ( OPTION_ROBOT_LINT_ROBOCOP_ENABLED, ) from robocorp_ls_core import uris try: from robotframework_ls.impl.ast_utils import collect_errors from robotframework_ls.impl import code_analysis import os.path log.debug("Lint: starting (in thread).") completion_context = self._create_completion_context(doc_uri, 0, 0, monitor) if completion_context is None: return [] config = completion_context.config robocop_enabled = config is None or config.get_setting( OPTION_ROBOT_LINT_ROBOCOP_ENABLED, bool, False ) ast = completion_context.get_ast() source = completion_context.doc.source monitor.check_cancelled() errors = collect_errors(ast) log.debug("Collected AST errors (in thread): %s", len(errors)) monitor.check_cancelled() analysis_errors = code_analysis.collect_analysis_errors(completion_context) monitor.check_cancelled() log.debug("Collected analysis errors (in thread): %s", len(analysis_errors)) errors.extend(analysis_errors) lsp_diagnostics = [error.to_lsp_diagnostic() for error in errors] try: if robocop_enabled: from robocorp_ls_core.robocop_wrapper import ( collect_robocop_diagnostics, ) workspace = completion_context.workspace if workspace is not None: project_root = workspace.root_path else: project_root = os.path.abspath(".") monitor.check_cancelled() lsp_diagnostics.extend( collect_robocop_diagnostics( project_root, ast, uris.to_fs_path(doc_uri), source ) ) except Exception: log.exception( "Error collecting Robocop errors (possibly an unsupported Robocop version is installed)." ) return lsp_diagnostics except JsonRpcRequestCancelled: raise JsonRpcRequestCancelled("Lint cancelled (inside lint)") except: log.exception("Error collecting errors.") return []
def run(): from robotframework_ls import import_rf_interactive from robotframework_ls.config_extension import ( apply_interpreter_info_to_config, ) from robocorp_ls_core.ep_resolve_interpreter import EPResolveInterpreter from robocorp_ls_core import uris import_rf_interactive() from robocorp_ls_core.options import Setup try: from robotframework_interactive.server.rf_interpreter_server_manager import ( RfInterpreterServerManager, ) interpreter_id = self._next_interpreter_id() def on_interpreter_message(interpreter_message: dict): """ :param interpreter_message: Something as: { "jsonrpc": "2.0", "method": "interpreter/output", "params": { "output": "Some output\n", "category": "stdout", }, } """ params = interpreter_message["params"] params["interpreter_id"] = interpreter_id self._endpoint.notify(interpreter_message["method"], params) rf_interpreter_server_manager = RfInterpreterServerManager( verbose=Setup.options.verbose, base_log_file=Setup.options.log_file, on_interpreter_message=on_interpreter_message, uri=uri, ) fs_path = uris.to_fs_path(uri) rf_interpreter_server_manager.config = config rf_config = rf_interpreter_server_manager.config # Just making sure that it has its own private copy before # mutating it... assert rf_config is not config for ep in self._pm.get_implementations(EPResolveInterpreter): interpreter_info = ep.get_interpreter_info_for_doc_uri(uri) if interpreter_info is not None: on_interpreter_message( { "jsonrpc": "2.0", "method": "interpreter/output", "params": { "output": f"Target: {interpreter_info.get_interpreter_id()}\n", "category": "info", }, } ) apply_interpreter_info_to_config(rf_config, interpreter_info) break on_interpreter_message( { "jsonrpc": "2.0", "method": "interpreter/output", "params": {"output": f"Path: {fs_path}\n", "category": "info"}, } ) rf_interpreter_server_manager.interpreter_start(uri) ls_thread_pool = futures.ThreadPoolExecutor(max_workers=2) self._interpreter_id_to_rf_info[interpreter_id] = _RfInfo( rf_interpreter_server_manager, commands_thread_pool, ls_thread_pool ) except Exception as e: log.exception("Error starting interpreter.") return ActionResult(False, message=str(e)).as_dict() else: return ActionResult( True, result={"interpreter_id": interpreter_id} ).as_dict()
def test_win_to_fs_path(uri, path): assert uris.to_fs_path(uri) == path
def _get_completions(completion_context, token, match_libs, extensions, skip_current): from robotframework_ls.impl.string_matcher import RobotStringMatcher from robocorp_ls_core import uris from robotframework_ls.impl.robot_constants import BUILTIN_LIB, RESERVED_LIB ret = [] sel = completion_context.sel value_to_cursor = token.value if token.end_col_offset > sel.col: value_to_cursor = value_to_cursor[:-(token.end_col_offset - sel.col)] if "{" in value_to_cursor: value_to_cursor = completion_context.token_value_resolving_variables( value_to_cursor) value_to_cursor_split = os.path.split(value_to_cursor) if os.path.isabs(value_to_cursor): _add_completions_from_dir( completion_context, value_to_cursor_split[0], RobotStringMatcher(value_to_cursor_split[1]), ret, sel, token, value_to_cursor_split[1], extensions, skip_current=skip_current, ) else: if match_libs: matcher = RobotStringMatcher(value_to_cursor) libspec_manager = completion_context.workspace.libspec_manager library_names = set(libspec_manager.get_library_names()) library_names.discard(BUILTIN_LIB) library_names.discard(RESERVED_LIB) for library_name in library_names: if matcher.accepts(library_name): ret.append( _create_completion_item(library_name, sel, token)) # After checking the existing library names in memory (because we # loaded them at least once), check libraries in the filesystem. uri = completion_context.doc.uri path = uris.to_fs_path(uri) dirname = os.path.dirname(path) matcher = RobotStringMatcher(value_to_cursor_split[1]) directory = os.path.join(dirname, value_to_cursor_split[0]) _add_completions_from_dir( completion_context, directory, matcher, ret, sel, token, value_to_cursor_split[1], extensions, skip_current=skip_current, ) return ret
def test_rf_interactive_integrated_input_request( language_server_io: ILanguageServerClient, rf_interpreter_startup: _RfInterpreterInfo, ): from robotframework_ls.commands import ROBOT_INTERNAL_RFINTERACTIVE_EVALUATE from robocorp_ls_core import uris language_server = language_server_io uri = rf_interpreter_startup.uri robot_file = uris.to_fs_path(uri) lib_file = os.path.join(os.path.dirname(robot_file), "my_lib.py") with open(lib_file, "w", encoding="utf-8") as stream: stream.write(r""" def check_input(): import sys sys.__stdout__.write('Enter something\n') return input() """) message_matcher = language_server.obtain_pattern_message_matcher( {"method": "interpreter/output"}) language_server.execute_command( ROBOT_INTERNAL_RFINTERACTIVE_EVALUATE, [{ "interpreter_id": 0, "code": "*** Settings ***\nLibrary ./my_lib.py" }], ) def run_in_thread(): language_server.execute_command( ROBOT_INTERNAL_RFINTERACTIVE_EVALUATE, [{ "interpreter_id": 0, "code": """ *** Test Case *** Test ${var}= Check Input Log ${var} console=True """, }], ) t = threading.Thread(target=run_in_thread) t.start() assert message_matcher.event.wait(10) assert message_matcher.msg == { "jsonrpc": "2.0", "method": "interpreter/output", "params": { "output": "Enter something\n", "category": "stdout", "interpreter_id": 0, }, } import time time.sleep(0.5) message_matcher = language_server.obtain_pattern_message_matcher( {"method": "interpreter/output"}) language_server.execute_command( ROBOT_INTERNAL_RFINTERACTIVE_EVALUATE, [{ "interpreter_id": 0, "code": "EnterThis" }], ) assert message_matcher.event.wait(10) assert message_matcher.msg == { "jsonrpc": "2.0", "method": "interpreter/output", "params": { "output": "EnterThis\n", "category": "stdout", "interpreter_id": 0 }, } t.join(10) assert not t.is_alive()
def resource_name(self): from robocorp_ls_core import uris uri = self.completion_context.doc.uri return os.path.splitext(os.path.basename(uris.to_fs_path(uri)))[0]
def get_folder_paths(self) -> List[str]: folders = self._folders # Ok, thread-safe (folders are always set as a whole) return [ uris.to_fs_path(ws_folder.uri) for ws_folder in folders.values() ]
def source(self): from robocorp_ls_core import uris return uris.to_fs_path(self.completion_context.doc.uri)
def lint(self, doc_uri, is_saved): from robocorp_ls_core.lsp import DiagnosticSeverity from robocorp_ls_core import uris import json if is_saved: # When a documnt is saved, if it's a conda.yaml or a robot.yaml, # validate it. if doc_uri.endswith("conda.yaml") or doc_uri.endswith( "robot.yaml"): robot_yaml_fs_path = uris.to_fs_path(doc_uri) if robot_yaml_fs_path.endswith("conda.yaml"): p = os.path.dirname(robot_yaml_fs_path) for _ in range(3): target = os.path.join(p, "robot.yaml") if target and os.path.exists(target): robot_yaml_fs_path = target break else: # We didn't find the 'robot.yaml' for the 'conda.yaml' # bail out. return action_result = self._rcc.configuration_diagnostics( robot_yaml_fs_path) if action_result.success: json_contents = action_result.result as_dict = json.loads(json_contents) checks = as_dict.get("checks", []) found = [] if isinstance(checks, (list, tuple)): for check in checks: if isinstance(check, dict): status = check.get("status", "ok").lower() if status != "ok": # Default is error (for fail/fatal) severity = DiagnosticSeverity.Error if status in ("warn", "warning"): severity = DiagnosticSeverity.Warning elif status in ("info", "information"): severity = DiagnosticSeverity.Information # The actual line is not given by rcc, so, put # all errors in the first 2 lines. message = check.get( "message", "<unable to get error message>") url = check.get("url") if url: message += ( f" -- see: {url} for more information." ) found.append({ "range": { "start": { "line": 0, "character": 0 }, "end": { "line": 1, "character": 0 }, }, "severity": severity, "source": "robocorp-code", "message": message, }) self._lsp_messages.publish_diagnostics(doc_uri, found)