def test_code_action_return_list(self): response = self.client.lsp.send_request( CODE_ACTION, CodeActionParams( text_document=TextDocumentIdentifier(uri='file://return.list'), range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), context=CodeActionContext( diagnostics=[ Diagnostic( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), message='diagnostic' ) ], only=[CodeActionKind.Refactor] ) ) ).result(timeout=CALL_TIMEOUT) assert response[0]['title'] == 'action1' assert response[1]['title'] == 'action2' assert response[1]['kind'] == CodeActionKind.Refactor assert response[2]['title'] == 'cmd1' assert response[2]['command'] == 'cmd1' assert response[2]['arguments'] == [1, 'two']
def test_code_action_return_none(self): response = self.client.lsp.send_request( CODE_ACTION, CodeActionParams( text_document=TextDocumentIdentifier(uri='file://return.none'), range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), context=CodeActionContext( diagnostics=[ Diagnostic( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), message='diagnostic', ) ], only=[CodeActionKind.Refactor], ) ) ).result(timeout=CALL_TIMEOUT) assert response is None
def f( params: DeclarationParams ) -> Optional[Union[Location, List[Location], List[LocationLink]]]: location = Location( uri='uri', range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ) location_link = LocationLink( target_uri='uri', target_range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), target_selection_range=Range( start=Position(line=0, character=0), end=Position(line=2, character=2), ), origin_selection_range=Range( start=Position(line=0, character=0), end=Position(line=3, character=3), ), ) return { # type: ignore 'file://return.location': location, 'file://return.location_list': [location], 'file://return.location_link_list': [location_link], }.get(params.text_document.uri, None)
def test_document_multiline_edit(): old = [ "def hello(a, b):\n", " print a\n", " print b\n" ] doc = Document('file:///uri', u''.join(old), sync_kind=TextDocumentSyncKind.INCREMENTAL) change = TextDocumentContentChangeEvent( range=Range(start=Position(line=1, character=4), end=Position(line=2, character=11)), range_length=0, text=u'print a, b') doc.apply_change(change) assert doc.lines == [ "def hello(a, b):\n", " print a, b\n" ] doc = Document('file:///uri', u''.join(old), sync_kind=TextDocumentSyncKind.INCREMENTAL) change = TextDocumentContentChangeEvent( range=Range(start=Position(line=1, character=4), end=Position(line=2, character=11)), text=u'print a, b') doc.apply_change(change) assert doc.lines == [ "def hello(a, b):\n", " print a, b\n" ]
def test_range_to_utf16(): assert range_to_utf16( ['x="😋"'], Range(start=Position(line=0, character=3), end=Position(line=0, character=4)) ) == Range(start=Position(line=0, character=3), end=Position(line=0, character=5)) range = Range(start=Position(line=0, character=3), end=Position(line=0, character=4)) range_to_utf16(['x="😋"'], range) assert range == Range(start=Position(line=0, character=3), end=Position(line=0, character=4))
def _validate(ls, params: Union[DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams]): ls.show_message_log("Validating CP2K input...") diagnostics = [] text_doc = ls.workspace.get_document(params.text_document.uri) parser = CP2KInputParser() 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]} ({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: # 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(start=Position(line=linenr, character=colnr + 1 - count), end=Position(line=linenr, character=colnr + 1)) else: erange = Range(start=Position(line=linenr, character=1), end=Position(line=linenr, character=len(line))) diagnostics += [ Diagnostic(range=erange, message=msg, source=type(ls).__name__) ] ls.publish_diagnostics(text_doc.uri, diagnostics)
def f(params: PrepareRenameParams) -> Optional[Union[Range, PrepareRename]]: return { # type: ignore 'file://return.range': Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), 'file://return.prepare_rename': PrepareRename( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), placeholder='placeholder', ), }.get(params.text_document.uri, None)
def f( params: DocumentSymbolParams ) -> Union[List[SymbolInformation], List[DocumentSymbol]]: symbol_info = SymbolInformation( name='symbol', kind=SymbolKind.Namespace, location=Location( uri='uri', range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ), container_name='container', deprecated=False, ) document_symbol_inner = DocumentSymbol( name='inner_symbol', kind=SymbolKind.Number, range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), selection_range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ) document_symbol = DocumentSymbol( name='symbol', kind=SymbolKind.Object, range=Range( start=Position(line=0, character=0), end=Position(line=10, character=10), ), selection_range=Range( start=Position(line=0, character=0), end=Position(line=10, character=10), ), detail='detail', children=[document_symbol_inner], deprecated=True, ) return { # type: ignore 'file://return.symbol_information_list': [symbol_info], 'file://return.document_symbol_list': [document_symbol], }.get(params.text_document.uri, None)
def exception_to_diagnostic(exc: BaseException): """Convert an exception into a diagnostic we can send to the client.""" # Config errors sometimes wrap the true cause of the problem if isinstance(exc, ConfigError) and exc.__cause__ is not None: exc = exc.__cause__ if isinstance(exc, SyntaxError): path = pathlib.Path(exc.filename or "") line = (exc.lineno or 1) - 1 else: tb = exc.__traceback__ frame = traceback.extract_tb(tb)[-1] path = pathlib.Path(frame.filename) line = (frame.lineno or 1) - 1 message = type(exc).__name__ if exc.args.count == 0 else exc.args[0] diagnostic = Diagnostic( range=Range( start=Position(line=line, character=0), end=Position(line=line + 1, character=0), ), message=message, severity=DiagnosticSeverity.Error, ) return Uri.from_fs_path(str(path)), diagnostic
def f(params: HoverParams) -> Optional[Hover]: range = Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ) return { 'file://return.marked_string': Hover( range=range, contents=MarkedString( language='language', value='value', ), ), 'file://return.marked_string_list': Hover( range=range, contents=[ MarkedString( language='language', value='value', ), 'str type' ], ), 'file://return.markup_content': Hover( range=range, contents=MarkupContent(kind=MarkupKind.Markdown, value='value'), ), }.get(params.text_document.uri, None)
def _find_snippet_insert_position(self) -> Union[Position, Range]: """Returns the position inside the document where new test cases can be inserted. If the <tests> section does not exists in the file, the best aproximate position where the tests should be inserted is returned (acording to the IUC best practices tag order). Returns: Position: The position where the test cases can be inserted in the document. """ tool = self.tool_document section = tool.find_element(TESTS) if section: content_range = tool.get_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=start, end=end) else: section = tool.find_element(OUTPUTS) if section: return tool.get_position_after(section) section = tool.find_element(INPUTS) if section: return tool.get_position_after(section) section = tool.find_element(TOOL) if section: content_range = tool.get_content_range(section) if content_range: return content_range.end return Position(line=0, character=0)
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=Range( start=Position(line=error.line - 1, character=error.column), end=Position(line=error.line - 1, character=error.column), ), message=error.message, source=self.server_name, ) diagnostics.append(result) return diagnostics
async def lsp_completion( ls: LanguageServer, params: CompletionParams, ) -> Optional[List[CompletionItem]]: await asyncio.sleep(DEBOUNCE_DELAY) items: List[CompletionItem] = [] ast = await buildout.parse(ls, params.text_document.uri, True) for line in ast.lines: pos = params.position (var_name, var_type, value) = line ci = CompletionItem(label=var_name, text_edit=TextEdit( range=Range( start=Position(line=pos.line, character=pos.character), end=Position(line=pos.line, character=pos.character + len(var_name))), new_text=var_name, ), kind=CompletionItemKind.Variable, documentation=MarkupContent( kind=MarkupKind.Markdown, value=f"{var_name} : {var_type} = {value}", )) items.append(ci) return items
def _build_diagnostics_for_expanded_macros( self, tool: GalaxyToolXmlDocument, invalid_document_error) -> List[Diagnostic]: virtual_uri = tool.xml_document.document.uri.replace( "file", "gls-expand") diagnostics = [ Diagnostic( range=tool.get_macros_range(), message=error.message, source=self.server_name, code=DiagnosticCodes.INVALID_EXPANDED_TOOL, related_information=[ DiagnosticRelatedInformation( message= ("The validation error ocurred on the expanded version of " "the document, i.e. after replacing macros. " "Click here to preview the expanded document."), location=Location( uri=f"{virtual_uri}{EXPAND_DOCUMENT_URI_SUFFIX}", range=Range( start=Position(line=error.line - 1, character=error.column), end=Position(line=error.line - 1, character=error.column), ), ), ) ], ) for error in invalid_document_error.error_log.filter_from_errors() ] return diagnostics
def test_range_formatting_return_list(self): response = self.client.lsp.send_request( RANGE_FORMATTING, DocumentRangeFormattingParams( text_document=TextDocumentIdentifier(uri='file://return.list'), range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), options=FormattingOptions( tab_size=2, insert_spaces=True, trim_trailing_whitespace=True, insert_final_newline=True, trim_final_newlines=True, ), ), ).result(timeout=CALL_TIMEOUT) assert response assert response[0]['newText'] == 'text' assert response[0]['range']['start']['line'] == 0 assert response[0]['range']['start']['character'] == 0 assert response[0]['range']['end']['line'] == 1 assert response[0]['range']['end']['character'] == 1
def _calculate_external_changes_for_macro( self, tool: GalaxyToolXmlDocument, macro_file_definition: ImportedMacrosFile, macro: MacroData, params: CodeActionParams, ) -> Dict[str, List[TextEdit]]: """Returns a dictionary with the document uri and the collection of TextEdit operations for that particular document. The edits will add the macro definition to the given imported macros file and replace the refactored macro with the corresponding <expand> element in the tool wrapper.""" macros_xml_doc = macro_file_definition.document if macro_file_definition.file_uri is None or macros_xml_doc is None or macros_xml_doc.root is None: return {} macros_root = macros_xml_doc.root insert_position = macros_xml_doc.get_position_after_last_child( macros_root) insert_range = Range(start=insert_position, end=insert_position) macro_xml = f'<xml name="{macro.name}">\n{macro.content}\n</xml>' final_macro_xml = self._adapt_format(macros_xml_doc, insert_range, macro_xml) external_edit = TextEdit( range=insert_range, new_text=final_macro_xml, ) changes = { params.text_document.uri: [ self._edit_replace_range_with_macro_expand( tool, macro, params.range) ], macro_file_definition.file_uri: [external_edit], } return changes
def lsp_text_edits( document: Document, 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. Additionally, makes sure returned code is syntactically valid Python. """ new_code = changed_file.get_new_code() if not is_valid_python(new_code): return [] old_code = document.source position_lookup = PositionLookup(old_code) text_edits = [] for opcode in get_opcodes(old_code, new_code): if opcode.op in _OPCODES_CHANGE: start = position_lookup.get(opcode.old_start) end = position_lookup.get(opcode.old_end) new_text = new_code[opcode.new_start : opcode.new_end] text_edits.append( TextEdit( range=Range(start=start, end=end), new_text=new_text, ) ) return text_edits
def on_error(e: UnexpectedToken): diagnostics.append(Diagnostic( range=Range( start=Position(line=e.line - 1, character=e.column - 1), end=Position(line=e.line - 1, character=e.column) ), message=user_repr(e))) return True
def get_range_for_context(self, xml_document: XmlDocument, context: XmlContext) -> Range: if context.node is None: return Range() start_offset, end_offset = context.node.get_offsets(context.offset) return convert_document_offsets_to_range(xml_document.document, start_offset, end_offset)
def test_document_empty_edit(): doc = Document('file:///uri', u'') change = TextDocumentContentChangeEvent( range=Range(start=Position(line=0, character=0), end=Position(line=0, character=0)), range_length=0, text=u'f') doc.apply_change(change) assert doc.source == u'f'
def test_document_line_edit(): doc = Document('file:///uri', u'itshelloworld') change = TextDocumentContentChangeEvent( range=Range(start=Position(line=0, character=3), end=Position(line=0, character=8)), range_length=0, text=u'goodbye') doc.apply_change(change) assert doc.source == u'itsgoodbyeworld'
def emit(self, record: logging.LogRecord) -> None: conditions = [ "sphinx" not in record.name, record.levelno not in {logging.WARNING, logging.ERROR}, not self.translator, ] if any(conditions): # Log the record as normal super().emit(record) return # Only process errors/warnings once. if not self.only_once.filter(record): return # Let sphinx do what it does to warning/error messages self.translator.filter(record) loc = record.location if isinstance(record, SphinxLogRecord) else "" doc, lineno = self.get_location(loc) line = lineno or 1 logger.debug("Reporting diagnostic at %s:%s", doc, line) try: # Not every message contains a string... if not isinstance(record.msg, str): message = str(record.msg) else: message = record.msg # Only attempt to format args if there are args to format if record.args is not None and len(record.args) > 0: message = message % record.args except Exception: message = str(record.msg) logger.error("Unable to format diagnostic message: %s", traceback.format_exc()) diagnostic = Diagnostic( range=Range( start=Position(line=line - 1, character=0), end=Position(line=line, character=0), ), message=message, severity=DIAGNOSTIC_SEVERITY.get(record.levelno, DiagnosticSeverity.Warning), ) if doc not in self.diagnostics: self.diagnostics[doc] = [diagnostic] else: self.diagnostics[doc].append(diagnostic) super().emit(record)
def f(params: SelectionRangeParams) -> Optional[List[SelectionRange]]: if params.text_document.uri == 'file://return.list': root = SelectionRange(range=Range( start=Position(line=0, character=0), end=Position(line=10, character=10), ), ) inner_range = SelectionRange( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), parent=root, ) return [root, inner_range] else: return None
def declaration(ls: DiplomatLanguageServer, params: DeclarationParams) -> Location: if not ls.indexed: reindex_all(ls) uri_source = unquote(params.text_document.uri) ref_range = Range(start=params.position, end=params.position) ret = ls.svindexer.get_definition_from_location( Location(uri=uri_source, range=ref_range)) return ret
def f(params: LinkedEditingRangeParams ) -> Optional[LinkedEditingRanges]: if params.text_document.uri == 'file://return.ranges': return LinkedEditingRanges( ranges=[ Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), Range( start=Position(line=1, character=1), end=Position(line=2, character=2), ), ], word_pattern='pattern', ) else: return None
def f(params: DocumentColorParams) -> List[ColorInformation]: return [ ColorInformation( range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), color=Color(red=0.5, green=0.5, blue=0.5, alpha=0.5), ) ]
def from_str(spec: str): """Create a range from the given string a:b-x:y""" start, end = spec.split("-") sl, sc = start.split(":") el, ec = end.split(":") return Range( start=Position(line=int(sl), character=int(sc)), end=Position(line=int(el), character=int(ec)), )
def f( params: DocumentHighlightParams ) -> Optional[List[DocumentHighlight]]: if params.text_document.uri == 'file://return.list': return [ DocumentHighlight(range=Range( start=Position(line=0, character=0), end=Position(line=1, character=1), ), ), DocumentHighlight( range=Range( start=Position(line=1, character=1), end=Position(line=2, character=2), ), kind=DocumentHighlightKind.Write, ), ] else: return None
def references(ls: DiplomatLanguageServer, params: ReferenceParams) -> T.List[Location]: """Returns completion items.""" if not ls.indexed: reindex_all(ls) uri_source = unquote(params.text_document.uri) ref_range = Range(start=params.position, end=params.position) ret = ls.svindexer.get_refs_from_location( Location(uri=uri_source, range=ref_range)) return ret
def test_semantic_tokens_range_return_none(self): response = self.client.lsp.send_request( TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, SemanticTokensRangeParams( text_document=TextDocumentIdentifier(uri='file://return.none'), range=Range( start=Position(line=0, character=0), end=Position(line=10, character=80)))).result(timeout=CALL_TIMEOUT) assert response is None