def visit_symbol(self, ctx: antlr4.ParserRuleContext, kind:int=SymbolKind.Variable, visit_children:bool=True): id_ctx = ctx.getChild(0, Parser.IdentifierContext) symbol = DocumentSymbol( name=id_ctx.start.text, kind=kind, range=Range( start=Position(ctx.start.line-1, ctx.start.column), end=Position(ctx.stop.line-1, ctx.stop.column) ), selection_range=Range( start=Position(id_ctx.start.line-1, id_ctx.start.column), end=Position(id_ctx.stop.line-1, id_ctx.stop.column) ), detail="", children=[], deprecated=False ) logging.debug(f"found symbol: {symbol.name}") self.scope.append(symbol) if visit_children: logging.debug(f"Visiting children of symbol: {symbol.name}") prev_scope = self.scope self.scope = symbol.children res = self.visitChildren(ctx) self.scope = prev_scope return res
def test_range_from_utf16(): assert range_from_utf16(['x="😋"'], Range(Position(0, 3), Position(0, 5))) == Range( Position(0, 3), Position(0, 4)) range = Range(Position(0, 3), Position(0, 5)) range_from_utf16(['x="😋"'], range) assert range == Range(Position(0, 3), Position(0, 5))
def _validate(ls, params): ls.show_message_log("Validating CP2K input...") diagnostics = [] text_doc = ls.workspace.get_document(params.textDocument.uri) parser = CP2KInputParser(DEFAULT_CP2K_INPUT_XML) with open(text_doc.path, "r") as fhandle: try: parser.parse(fhandle) except (TokenizerError, ParserError) as exc: ctx = exc.args[1] line = ctx["line"].rstrip() msg = f"Syntax error: {exc.args[0]}" if exc.__cause__: msg += f"({exc.__cause__})" linenr = ctx["linenr"] - 1 colnr = ctx["colnr"] if colnr is not None: count = 0 # number of underline chars after (positiv) or before (negative) the marker if ref_colnr given nchars = colnr # relevant line length if ctx["ref_colnr"] is not None: count = ctx["ref_colnr"] - ctx["colnr"] nchars = min(ctx["ref_colnr"], ctx["colnr"]) # correct if ref comes before if ctx["colnrs"] is not None: # shift by the number of left-stripped ws # ctx["colnrs"] contains the left shift for each possibly continued line nchars += ctx["colnrs"][ 0] # assume no line-continuation for now # at least do one context count = max(1, count) erange = Range(Position(linenr, colnr + 1 - count), Position(linenr, colnr + 1)) else: erange = Range(Position(linenr, 1), Position(linenr, len(line))) diagnostics += [ Diagnostic(erange, msg, source=type(cp2k_inp_server).__name__, related_information=[]) ] ls.publish_diagnostics(text_doc.uri, diagnostics)
def _get_range(p: SourcePosition = None): if p is None: return Range( Position(), Position(0, sys.maxsize), ) else: return Range( Position(p.line - 1, p.column - 1), Position(p.end_line - 1, p.end_column - 1), )
def _get_macros_range(self, document: Document, xml_tree: etree.ElementTree) -> Optional[Range]: """Given a XML document and its corresponding ElementTree, finds the first macro import element and returns its Range position inside the document. Args: document (Document): The XML tool document. xml_tree (etree.ElementTree): The corresponding ElementTree of the document. Returns: Optional[Range]: The Range position of the import file if it exists. """ try: import_element = xml_tree.find("macros//import") line_number = import_element.sourceline - 1 filename = import_element.text start = document.lines[line_number].find(filename) end = start + len(filename) return Range( Position(line_number, start), Position(line_number, end), ) except BaseException: return None
def lsp_text_edits(changed_file: ChangedFile) -> List[TextEdit]: """Take a jedi `ChangedFile` and convert to list of text edits. Handles inserts, replaces, and deletions within a text file """ old_code = ( changed_file._module_node.get_code() # pylint: disable=protected-access ) new_code = changed_file.get_new_code() opcode_position_lookup_old = get_opcode_position_lookup(old_code) text_edits = [] for opcode in get_opcodes(old_code, new_code): if opcode.op in _OPCODES_CHANGE: start = opcode_position_lookup_old[opcode.old_start] end = opcode_position_lookup_old[opcode.old_end] start_char = opcode.old_start - start.range_start end_char = opcode.old_end - end.range_start new_text = new_code[opcode.new_start:opcode.new_end] text_edits.append( TextEdit( range=Range( start=Position(line=start.line, character=start_char), end=Position(line=end.line, character=end_char), ), new_text=new_text, )) return text_edits
def _build_diagnostics( self, error_log: etree._ListErrorLog, xml_tree: Optional[etree.ElementTree] = None) -> List[Diagnostic]: """Gets a list of diagnostics from the XSD validation error log. Args: error_log (etree._ListErrorLog): The error log generated after XSD schema validation. xml_tree (Optional[etree.ElementTree], optional): The element tree associated with the error log. Defaults to None. Raises: ExpandMacrosFoundException: This is raised when a macro ``expand`` element is found in the error log. The ``expand`` element is not part of the XSD so, when this exception is raised, we know that the document must expand the macros before validation. Returns: List[Diagnostic]: The list of resulting diagnostic items found in the error log. """ diagnostics = [] for error in error_log.filter_from_errors(): if "expand" in error.message: raise ExpandMacrosFoundException(xml_tree) result = Diagnostic( Range( Position(error.line - 1, error.column), Position(error.line - 1, error.column), ), error.message, source=self.server_name, ) diagnostics.append(result) return diagnostics
def _find_snippet_insert_position(self) -> Union[Position, Range]: """Returns the position inside the document where command section can be inserted. If the <command> section does not exists in the file, the best aproximate position where the section should be inserted is returned (acording to the IUC best practices tag order). Returns: Position: The position where the command section can be inserted in the document. """ tool = self.tool_document section = tool.find_element(COMMAND) if section: content_range = tool.get_element_content_range(section) if content_range: return content_range.end else: # is self closed <tests/> start = tool.get_position_before(section) end = tool.get_position_after(section) return Range(start, end) else: section = tool.find_element(ENV_VARIABLES) if section: return tool.get_position_before(section) section = tool.find_element(CONFIGFILES) if section: return tool.get_position_before(section) section = tool.find_element(INPUTS) if section: return tool.get_position_before(section) section = tool.find_element(TOOL) if section: return tool.get_element_content_range(section).end return Position()
def test_invalid_textxfile_diagnostics_on_doc_change(client_server): client, server = client_server doc = doc_from_path(TEXTXFILE_WITH_ERROR_PATH, 'textxfile') content = doc.text doc.text = '' # Add doc to server's workspace directly server.workspace.put_document(doc) versioned_doc = VersionedTextDocumentIdentifier(doc.uri, doc.version + 1) content_change = TextDocumentContentChangeEvent( Range(Position(0, 0), Position(0, 0)), 0, content ) send_text_doc_did_change_request(client, versioned_doc, [content_change]) params = builtin_notifications.get(timeout=CALL_TIMEOUT) assert params.uri == doc.uri d = params.diagnostics[0] r = d.range assert d.message == "Expected ',' or ']'" assert r.start.line == 0 assert r.start.character == 0 assert r.end.line == 0 assert r.end.character == 500 # Remove all docs from the workspace server.workspace._docs = {}
def _make_diagnostic(linter, node, message): start_pos = Position(node.parse_tree.loc) end_pos = Position(node.parse_tree.loc + 1) return Diagnostic(Range(start_pos, end_pos), message=message, code=linter.code, severity=linter.severity)
def _validate(ls, params): """ Validate a document and publish diagnostics """ text_doc = ls.workspace.get_document(params.textDocument.uri) source = text_doc.source diagnostics = [] line_index = -1 # test comment for line in source.splitlines(): logging.debug("*** %s", line) line_index += 1 col_index = -1 while True: if col_index == -1: col_index = str.find(line, "e") else: col_index = str.find(line, "e", col_index + 1) if col_index == -1: logging.debug("The remainder of line %s is efree", line_index) break msg = "Character e at column {}".format(col_index) d = Diagnostic(Range(Position(line_index, col_index), Position(line_index, col_index + 1)), msg, source=type(server).__name__) diagnostics.append(d) logging.debug("e at line %s, col %s", line_index, col_index) ls.publish_diagnostics(text_doc.uri, diagnostics)
def write(self, line): """This method lets us catch output from Sphinx.""" match = PROBLEM_PATTERN.match(line) if match: filepath = match.group("file") severity = PROBLEM_SEVERITY.get(match.group("type"), PROBLEM_SEVERITY["ERROR"]) diagnostics = self.diagnostics.get(filepath, None) if diagnostics is None: diagnostics = DiagnosticList() try: line_number = int(match.group("line")) except (TypeError, ValueError) as exc: self.logger.debug("Unable to parse line number: '%s'", match.group("line")) self.logger.debug(exc) line_number = 1 range_ = Range(Position(line_number - 1, 0), Position(line_number, 0)) diagnostics.append( Diagnostic(range_, match.group("message"), severity=severity, source="sphinx")) self.diagnostics[filepath] = diagnostics self.sphinx_log.info(line)
def test_document_multiline_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = Document('file:///uri', u''.join(old)) change = TextDocumentContentChangeEvent( Range(Position(1, 4), Position(2, 11)), 0, u'print a, b') doc.apply_change(change) assert doc.lines == ["def hello(a, b):\n", " print a, b\n"]
def __init__(self, filename, ctx, kind=None): super().__init__( name=ctx.getChild(0, Parser.IdentifierContext).start.text, kind=kind, location=Location( filename, Range(start=Position(ctx.start.line, ctx.start.column), end=Position(ctx.stop.line, ctx.stop.column))))
class TestXmlUtils: @pytest.mark.parametrize( "source, offset, expected_position", [ ("<tool></tool>", 0, Position(0, 0)), ("<tool></tool>", 13, Position(0, 13)), ("<tool></tool>\n", 14, Position(1, 0)), ("<tool>\n<description/>\n<inputs>\n</tool>", 6, Position(0, 6)), ("<tool>\n<description/>\n<inputs>\n</tool>", 7, Position(1, 0)), ("<tool>\n<description/>\n<inputs>\n</tool>", 21, Position(1, 14)), ("<tool>\n<description/>\n<inputs>\n</tool>", 22, Position(2, 0)), ("<tool>\n<description/>\n<inputs>\n</tool>", 30, Position(2, 8)), ("<tool>\n<description/>\n<inputs>\n</tool>", 38, Position(3, 7)), ], ) def test_convert_document_offset_to_position_returns_expected_result( self, source: str, offset: int, expected_position: Position) -> None: document = TestUtils.to_document(source) actual_position = convert_document_offset_to_position(document, offset) assert actual_position == expected_position @pytest.mark.parametrize( "source, start_offset, end_offset, expected_range", [ ("<tool></tool>", 0, 6, Range(Position(0, 0), Position(0, 6))), ("<tool>\n</tool>", 0, 14, Range(Position(0, 0), Position(1, 7))), ("<tool>\n<description/>\n</tool>", 7, 21, Range(Position(1, 0), Position(1, 14))), ("<tool>\n<description/>\n</tool>\n", 0, 30, Range(Position(0, 0), Position(3, 0))), ], ) def test_convert_document_offsets_to_range_returns_expected_result( self, source: str, start_offset: int, end_offset: int, expected_range: Range) -> None: document = TestUtils.to_document(source) actual_range = convert_document_offsets_to_range( document, start_offset, end_offset) assert actual_range == expected_range
def test_document_full_edit(): old = ["def hello(a, b):\n", " print a\n", " print b\n"] doc = Document('file:///uri', u''.join(old), sync_kind=TextDocumentSyncKind.FULL) change = TextDocumentContentChangeEvent( Range(Position(1, 4), Position(2, 11)), 0, u'print a, b') doc.apply_change(change) assert doc.lines == ["print a, b"]
def format(self, content: str, params: DocumentFormattingParams) -> List[TextEdit]: """Given the document contents returns the list of TextEdits needed to properly layout the document. """ formatted_result = self._format_document(content, params.options.tabSize) lines = content.count("\n") start = Position(0, 0) end = Position(lines + 1, 0) return [TextEdit(Range(start, end), formatted_result)]
def _diagnostic(msg: str, line = 1, col = 1, end_line = None, end_col = sys.maxsize, severity = DiagnosticSeverity.Error): if end_line is None: end_line = line return Diagnostic( Range( Position(line - 1, col - 1), Position(end_line - 1, end_col - 1), ), msg, severity=severity, )
def formatting(params: DocumentFormattingParams): doc = self.workspace.get_document(params.textDocument.uri) content = doc.source tokens, remain = self._parser.parse(content) if remain: self.show_message("CMake parser failed") return None formatted = Formatter().format(tokens) lines = content.count("\n") return [TextEdit(Range(Position(0, 0), Position(lines + 1, 0)), formatted)]
def lsp_diagnostic(error: jedi.api.errors.SyntaxError) -> Diagnostic: """Get LSP Diagnostic from Jedi SyntaxError""" return Diagnostic( range=Range( start=Position(line=error.line - 1, character=error.column), end=Position(line=error.line - 1, character=error.column), ), message=str(error).strip("<>"), severity=DiagnosticSeverity.Error, source="jedi", )
def visitInstantiation(self, ctx:Parser.InstantiationContext): for child in ctx.getChildren(lambda x: isinstance(x, Parser.IdentifierContext)): symbol = DocumentSymbol( name=child.start.text, kind=SymbolKind.Variable, range=Range( start=Position(ctx.start.line-1, ctx.start.column), end=Position(ctx.stop.line-1, ctx.stop.column) ), selection_range=Range( start=Position(child.start.line-1, child.start.column), end=Position(child.stop.line-1, child.stop.column) ), detail="", children=[], deprecated=False ) logging.debug(f"found symbol: {symbol.name}") self.scope.append(symbol) return None
def test_range(): assert Range(Position(1, 2), Position(3, 4)) \ == Range(Position(1, 2), Position(3, 4)) assert Range(Position(0, 2), Position(3, 4)) \ != Range(Position(1, 2), Position(3, 4)) assert Range(Position(0, 2), Position(3, 4)) != 'something else' assert "1:2-3:4" == repr(Range(Position(1, 2), Position(3, 4)))
def test_document_end_of_file_edit(): old = ["print 'a'\n", "print 'b'\n"] doc = Document('file:///uri', u''.join(old)) change = TextDocumentContentChangeEvent( Range(Position(2, 0), Position(2, 0)), 0, u'o') doc.apply_change(change) assert doc.lines == [ "print 'a'\n", "print 'b'\n", "o", ]
def jump_to_definition(ls, c: TextDocumentPositionParams) -> List[Location]: doc = ls.workspace.get_document(c.textDocument.uri) items = find_definition(doc.uri, doc.source, c.position.line + 1, c.position.character) return [ Location( path_to_uri(item.module_path), Range( Position(item.line - 1, item.column), Position(item.line - 1, item.column + len(item.name)), ), ) for item in items ]
def get_range_for_context(self, document: Document, context: XmlContext) -> Range: start_offset, end_offset = context.token.get_offsets(context.offset) start_line = max(document.source.count(NEW_LINE, 0, start_offset), 0) start_character = start_offset - document.source.rfind( NEW_LINE, 0, start_offset) end_line = max(document.source.count(NEW_LINE, 0, end_offset), 0) end_character = end_offset - document.source.rfind( NEW_LINE, 0, end_offset) return Range( Position(start_line, start_character), Position(end_line, end_character), )
def _cursor_word(self, uri: str, position: Position, include_all: bool = True) -> Optional[Tuple[str, Range]]: line = self._cursor_line(uri, position) cursor = position.character for m in re.finditer(r'\w+', line): end = m.end() if include_all else cursor if m.start() <= cursor <= m.end(): word = (line[m.start():end], Range(Position(position.line, m.start()), Position(position.line, end))) return word return None
def _get_diagnostic_range(err: TextXError) -> Range: """Prettifies error message. Args: err: an instance of TextXError (syntax or semantic) Returns: A range which should be highlighted Raises: None """ # Mark a whole line ( 500 for now, should be len(doc.lines[line]) ) line = 0 if err.line - 1 < 0 else err.line - 1 return Range(Position(line, 0), Position(line, 500))
def lsp_range(name: Name) -> Range: """Get LSP range from Jedi definition. - jedi is 1-indexed for lines and 0-indexed for columns - LSP is 0-indexed for lines and 0-indexed for columns - Therefore, subtract 1 from Jedi's definition line """ return Range( start=Position(line=name.line - 1, character=name.column), end=Position( line=name.line - 1, character=name.column + len(name.name), ), )
def validate(self, source): self.itpr = check(source) diagnostics = [] logging.debug(f'itpr errors: {self.itpr.errors}') for item in self.itpr.errors: l1 = item['lineno'] - 1 c1 = item['col_offset'] l2 = item['end_lineno'] - 1 c2 = item['end_col_offset'] msg = item['error'].message diagnostics.append( Diagnostic(range=Range(Position(l1, c1), Position(l2, c2)), message=msg, source="PDChecker")) return diagnostics
def test_location(): assert Location(uri="file:///document.txt", range=Range(Position(1, 2), Position(3, 4))) \ == Location(uri="file:///document.txt", range=Range(Position(1, 2), Position(3, 4))) assert Location(uri="file:///document.txt", range=Range(Position(1, 2), Position(3, 4))) \ != Location(uri="file:///another.txt", range=Range(Position(1, 2), Position(3, 4))) assert Location(uri="file:///document.txt", range=Range(Position(1, 2), Position(3, 4))) \ != 'something else' assert "file:///document.txt:1:2-3:4" == repr( Location(uri="file:///document.txt", range=Range(Position(1, 2), Position(3, 4))))