def get_doctree(self, *, docname: Optional[str] = None, uri: Optional[str] = None) -> Optional[Any]: """Return the initial doctree corresponding to the specified document. The ``docname`` of a document is its path relative to the project's ``srcdir`` minus the extension e.g. the docname of the file ``docs/lsp/features.rst`` would be ``lsp/features``. Parameters ---------- docname: Returns the doctree that corresponds with the given docname uri: Returns the doctree that corresponds with the given uri. """ if self.app is None or self.app.env is None or self.app.builder is None: return None if uri is not None: fspath = Uri.to_fs_path(uri) docname = self.app.env.path2doc(fspath) if docname is None: return None try: return self.app.env.get_and_resolve_doctree( docname, self.app.builder) except FileNotFoundError: self.logger.debug("Could not find doctree for '%s'", docname) # self.logger.debug(traceback.format_exc()) return None
def get_location_type(self, doc: Document, position: Position) -> str: """Given a document and a position, return the kind of location that represents. This will return one of the following values: - ``rst``: Indicates that the position is within an ``*.rst`` document - ``py``: Indicates that the position is within code in a ``*.py`` document - ``docstring``: Indicates that the position is within a docstring in a ``*.py`` document. Parameters ---------- doc: The document associated with the given position position: The position to determine the type of. """ ext = pathlib.Path(Uri.to_fs_path(doc.uri)).suffix if ext == ".rst": return "rst" if ext == ".py": # Let's count how many pairs of triple quotes are "above" us in the file # even => we're outside a docstring # odd => we're within a docstring source = self.text_to_position(doc, position) count = len(TRIPLE_QUOTE.findall(source)) return "py" if count % 2 == 0 else "docstring" # Fallback to rst self.logger.debug("Unable to determine location type for uri: %s", doc.uri) return "rst"
def save(self, params: DidSaveTextDocumentParams): super().save(params) filepath = Uri.to_fs_path(params.text_document.uri) if filepath.endswith("conf.py"): if self.app: conf_dir = pathlib.Path(self.app.confdir) else: # The user's config is currently broken... where should their conf.py be? if self.user_config is not None: config = typing.cast(InitializationOptions, self.user_config).sphinx else: config = SphinxConfig() conf_dir = config.resolve_conf_dir( self.workspace.root_uri) or pathlib.Path(".") if str(conf_dir / "conf.py") == filepath: self.clear_diagnostics("conf.py") self.sync_diagnostics() self.app = self._initialize_sphinx() else: self.clear_diagnostics("sphinx", params.text_document.uri) self.build()
async def workspace_symbols(ls, params: WorkspaceSymbolParams): # query = params.query # if database.declarations == []: # return None path = to_fs_path( "file:///c%3A/Users/eirik/Desktop/VSCode-SystemVerilog/verilog-examples/driver.sv" ) return parse_workspace_symbols(path)
def normalise_uri(uri: str) -> str: uri = Uri.from_fs_path(Uri.to_fs_path(uri)) # Paths on windows are case insensitive. if IS_WIN: uri = uri.lower() return uri
def syntax_check(self, file: str): if file is not None: f = uris.to_fs_path(file) self.syntaxchecker.run_incremental([f]) else: self.syntaxchecker.run() for file, diaglist in self.syntaxchecker.diagnostic_content.items(): self.publish_diagnostics(file, diaglist)
def resolve_doctree_dir(self, root_uri: str, actual_conf_dir: str, actual_build_dir: str) -> pathlib.Path: """Get the directory to use for doctrees based on the user's config. If ``doctree_dir`` is not set, this method will follow what ``sphinx-build`` does. - If ``make_mode`` is true, this will be set to ``${buildDir}/doctrees`` - If ``make_mode`` is false, this will be set to ``${buildDir}/.doctrees`` Otherwise, if ``doctree_dir`` is set the following "variables" are handled by this method. - ``${workspaceRoot}`` which expands to the workspace root as provided by the language client. - ``${workspaceFolder}`` alias for ``${workspaceRoot}``, placeholder ready for multi-root support. - ``${confDir}`` which expands to the configured config dir. - ``${buildDir}`` which expands to the configured build dir. Parameters ---------- root_uri The workspace root uri actual_conf_dir The fully resolved conf dir for the project actual_build_dir The fully resolved build dir for the project. """ if self.doctree_dir is None: if self.make_mode: return pathlib.Path(actual_build_dir, "doctrees") return pathlib.Path(actual_build_dir, ".doctrees") root_dir = Uri.to_fs_path(root_uri) match = PATH_VAR_PATTERN.match(self.doctree_dir) if match and match.group(1) in {"workspaceRoot", "workspaceFolder"}: build = pathlib.Path(self.doctree_dir).parts[1:] return pathlib.Path(root_dir, *build).resolve() if match and match.group(1) == "confDir": build = pathlib.Path(self.doctree_dir).parts[1:] return pathlib.Path(actual_conf_dir, *build).resolve() if match and match.group(1) == "buildDir": build = pathlib.Path(self.doctree_dir).parts[1:] return pathlib.Path(actual_build_dir, *build).resolve() return pathlib.Path(self.doctree_dir).expanduser()
def _get_workspace_folder_path(ls: LanguageServer, uri: str) -> str: # find workspace folder uri belongs to folders = sorted( (f.uri for f in ls.workspace.folders.values() if uri.startswith(f.uri)), key=len, reverse=True) if folders: return to_fs_path(folders[0]) return ls.workspace.root_path
def __init__(self, root_uri, sync_kind=None, workspace_folders=None): self._root_uri = root_uri self._root_uri_scheme = uri_scheme(self._root_uri) self._root_path = to_fs_path(self._root_uri) self._sync_kind = sync_kind self._folders = {} self._docs = {} if workspace_folders is not None: for folder in workspace_folders: self.add_folder(folder)
def __init__(self, uri, source=None, version=None, local=True, sync_kind=TextDocumentSyncKind.INCREMENTAL): self.uri = uri self.version = version self.path = to_fs_path(uri) self.filename = os.path.basename(self.path) self._local = local self._source = source self._is_sync_kind_full = sync_kind == TextDocumentSyncKind.FULL self._is_sync_kind_incremental = sync_kind == TextDocumentSyncKind.INCREMENTAL self._is_sync_kind_none = sync_kind == TextDocumentSyncKind.NONE
def complete_arguments(self, context: CompletionContext, domain: str, name: str) -> List[CompletionItem]: if domain or name not in {"include", "literalinclude"}: return [] if not self.rst.app: return [] srcdir = self.rst.app.srcdir partial = context.match.group("argument") base = os.path.dirname(Uri.to_fs_path(context.doc.uri)) items = complete_sphinx_filepaths(srcdir, base, partial) return [path_to_completion_item(context, p) for p in items]
def complete_targets( self, context: CompletionContext, name: str, domain: Optional[str] ) -> List[CompletionItem]: if domain or name != "download": return [] if not self.rst.app: return [] srcdir = self.rst.app.srcdir partial = context.match.group("label") base = os.path.dirname(Uri.to_fs_path(context.doc.uri)) items = complete_sphinx_filepaths(srcdir, base, partial) return [path_to_completion_item(context, p) for p in items]
def resolve_doc(self, doc: Document, label: str) -> Optional[str]: if self.rst.app is None: return None srcdir = self.rst.app.srcdir currentdir = pathlib.Path(Uri.to_fs_path(doc.uri)).parent if label.startswith("/"): path = pathlib.Path(srcdir, label[1:] + ".rst") else: path = pathlib.Path(currentdir, label + ".rst") if not path.exists(): return None return Uri.from_fs_path(str(path))
def resolve_conf_dir(self, root_uri: str) -> Optional[pathlib.Path]: """Get the conf dir to use based on the user's config. If ``conf_dir`` is not set, this method will attempt to find it by searching within the ``root_uri`` for a ``conf.py`` file. If multiple files are found, the first one found will be chosen. If ``conf_dir`` is set the following "variables" are handled by this method - ``${workspaceRoot}`` which expands to the workspace root as provided by the language client. - ``${workspaceFolder}`` alias for ``${workspaceRoot}``, placeholder ready for multi-root support. Parameters ---------- root_uri The workspace root uri """ root = Uri.to_fs_path(root_uri) if not self.conf_dir: ignore_paths = [".tox", "site-packages"] for candidate in pathlib.Path(root).glob("**/conf.py"): # Skip any files that obviously aren't part of the project if any(path in str(candidate) for path in ignore_paths): continue return candidate.parent # Nothing found return None match = PATH_VAR_PATTERN.match(self.conf_dir) if not match or match.group(1) not in { "workspaceRoot", "workspaceFolder" }: return pathlib.Path(self.conf_dir).expanduser() conf = pathlib.Path(self.conf_dir).parts[1:] return pathlib.Path(root, *conf).resolve()
def resolve_src_dir(self, root_uri: str, actual_conf_dir: str) -> pathlib.Path: """Get the src dir to use based on the user's config. By default the src dir will be the same as the conf dir, but this can be overriden by setting the ``src_dir`` field. There are a number of "variables" that can be included in the path, currently we support - ``${workspaceRoot}`` which expands to the workspace root as provided by the language client. - ``${workspaceFolder}`` alias for ``${workspaceRoot}``, placeholder ready for multi-root support. - ``${confDir}`` which expands to the configured config dir. Parameters ---------- root_uri The workspace root uri actual_conf_dir The fully resolved conf dir for the project """ if not self.src_dir: return pathlib.Path(actual_conf_dir) src_dir = self.src_dir root_dir = Uri.to_fs_path(root_uri) match = PATH_VAR_PATTERN.match(src_dir) if match and match.group(1) in {"workspaceRoot", "workspaceFolder"}: src = pathlib.Path(src_dir).parts[1:] return pathlib.Path(root_dir, *src).resolve() if match and match.group(1) == "confDir": src = pathlib.Path(src_dir).parts[1:] return pathlib.Path(actual_conf_dir, *src).resolve() return pathlib.Path(src_dir).expanduser()
def __init__( self, uri, project_root, language_name, project_name, mm_loader, source=None, version=None, ): super().__init__(uri, source, version, True) self.project_root = to_fs_path(project_root) self.project_name = project_name self.language_name = language_name self.mm_loader = mm_loader self._metamodel = None self.refresh_metamodel()
def resolve_path(self, doc: Document, argument: str) -> Optional[str]: if argument.startswith("/"): if not self.rst.app: return None basedir = pathlib.Path(self.rst.app.srcdir) # Remove the leading '/' otherwise is will wipe out the basedir when # concatenated argument = argument[1:] else: basedir = pathlib.Path(Uri.to_fs_path(doc.uri)).parent fpath = (basedir / argument).resolve() if not fpath.exists(): return None return Uri.from_fs_path(str(fpath))
def _mypy_check(ls: LanguageServer, uri: str, script: Script, result: List[types.Diagnostic]): from mypy import api assert jediEnvironment is not None version_info = jediEnvironment.version_info if config['diagnostic_on_change']: args = ['--command', script._code] else: args = [to_fs_path(uri)] lines = api.run([ '--python-executable', jediEnvironment.executable, '--python-version', f'{version_info.major}.{version_info.minor}', '--config-file', get_mypy_config(ls, uri), '--hide-error-context', '--show-column-numbers', '--show-error-codes', '--no-pretty', '--no-error-summary' ] + args) if lines[1]: ls.show_message(lines[1], types.MessageType.Error) return for line in lines[0].split('\n'): parts = line.split(':', 4) if len(parts) < 5: continue _fn, row, column, err_type, message = parts row = int(row) - 1 column = int(column) - 1 if err_type.strip() == 'note': severity = types.DiagnosticSeverity.Hint else: severity = types.DiagnosticSeverity.Warning result.append( types.Diagnostic(range=types.Range( start=types.Position(line=row, character=column), end=types.Position(line=row, character=len(script._code_lines[row]))), message=message.strip(), severity=severity, source='mypy')) return result
def get_initial_doctree(self, uri: str) -> Optional[Any]: """Return the initial doctree corresponding to the specified document. An "initial" doctree can be thought of as the abstract syntax tree of a reStructuredText document. This method disables all role and directives from being executed, instead they are replaced with nodes that simply represent that they exist. Parameters ---------- uri Returns the doctree that corresponds with the given uri. """ filename = pathlib.Path(Uri.to_fs_path(uri)) try: return read_initial_doctree(filename, self.logger) except FileNotFoundError: self.logger.debug(traceback.format_exc()) return None except Exception: self.logger.error(traceback.format_exc()) return None
def compile(self, uri, istr) -> bool: if self.uri == uri and istr == self.last_compiled_source: return self.last_status self.last_compiled_source = istr fspath = to_fs_path(uri) e = self._docompile(fspath, istr) self.uri = uri self.changed = False if not e: self.last_status = True self.last_successful_source[uri] = istr server.publish_diagnostics(uri, []) return True else: self.last_status = False if not birdeec.get_auto_completion_ast(): diag = Diagnostic( Range(Position(e.linenumber, e.pos + 1), Position(e.linenumber, e.pos + 2)), e.msg) server.publish_diagnostics(uri, [diag]) return False
def resolve_build_dir(self, root_uri: str, actual_conf_dir: str) -> pathlib.Path: """Get the build dir to use based on the user's config. If nothing is specified in the given ``config``, this will choose a location within the user's cache dir (as determined by `appdirs <https://pypi.org/project/appdirs>`). The directory name will be a hash derived from the given ``conf_dir`` for the project. Alternatively the user (or least language client) can override this by setting either an absolute path, or a path based on the following "variables". - ``${workspaceRoot}`` which expands to the workspace root as provided by the language client. - ``${workspaceFolder}`` alias for ``${workspaceRoot}``, placeholder ready for multi-root support. - ``${confDir}`` which expands to the configured config dir. Parameters ---------- root_uri The workspace root uri actual_conf_dir: The fully resolved conf dir for the project """ if not self.build_dir: # Try to pick a sensible dir based on the project's location cache = appdirs.user_cache_dir("esbonio", "swyddfa") project = hashlib.md5(str(actual_conf_dir).encode()).hexdigest() return pathlib.Path(cache) / project root_dir = Uri.to_fs_path(root_uri) match = PATH_VAR_PATTERN.match(self.build_dir) if match and match.group(1) in {"workspaceRoot", "workspaceFolder"}: build = pathlib.Path(self.build_dir).parts[1:] return pathlib.Path(root_dir, *build).resolve() if match and match.group(1) == "confDir": build = pathlib.Path(self.build_dir).parts[1:] return pathlib.Path(actual_conf_dir, *build).resolve() # Convert path to/from uri so that any path quirks from windows are # automatically handled build_uri = Uri.from_fs_path(self.build_dir) build_dir = Uri.to_fs_path(build_uri) # But make sure paths starting with '~' are not corrupted if build_dir.startswith("/~"): build_dir = build_dir.replace("/~", "~") # But make sure (windows) paths starting with '~' are not corrupted if build_dir.startswith("\\~"): build_dir = build_dir.replace("\\~", "~") return pathlib.Path(build_dir).expanduser()
def test_win_to_fs_path(uri, path): assert uris.to_fs_path(uri) == path