From 446f15d3b4b410fc7a9443a6bd010e8d1db735bd Mon Sep 17 00:00:00 2001 From: Tristan Gingold Date: Thu, 26 Jan 2023 18:27:30 +0100 Subject: pyGHDL/lsp: update, handle hover --- pyGHDL/lsp/document.py | 39 +++++++++++++++++++++---- pyGHDL/lsp/lsp.py | 2 +- pyGHDL/lsp/references.py | 76 +++++++++++++++++++++++++++++++++++++++++------- pyGHDL/lsp/vhdl_ls.py | 7 +++-- pyGHDL/lsp/workspace.py | 6 ++++ 5 files changed, 111 insertions(+), 19 deletions(-) (limited to 'pyGHDL') diff --git a/pyGHDL/lsp/document.py b/pyGHDL/lsp/document.py index 2656606c6..1a6a20f39 100644 --- a/pyGHDL/lsp/document.py +++ b/pyGHDL/lsp/document.py @@ -9,6 +9,8 @@ import pyGHDL.libghdl.vhdl.nodes as nodes import pyGHDL.libghdl.vhdl.sem_lib as sem_lib import pyGHDL.libghdl.vhdl.sem as sem import pyGHDL.libghdl.vhdl.formatters as formatters +import pyGHDL.libghdl.vhdl.prints as prints +import pyGHDL.libghdl.file_comments as file_comments from . import symbols, references @@ -197,7 +199,34 @@ class Document(object): def goto_definition(self, position): loc = self.position_to_location(position) - return references.goto_definition(self._tree, loc) + return references.find_definition_by_loc(self._tree, loc) + + def hover(self, position): + loc = self.position_to_location(position) + t = references.find_definition_by_loc(self._tree, loc) + if t is None: + return None + + # Regenerate the declaration + hand = prints.Allocate_Handle() + prints.Print_String(t, hand) + buffer = prints.Get_C_String(hand) + buf_len = prints.Get_Length(hand) + if buf_len == 0: + res = None + else: + txt = '' + t_loc = nodes.Get_Location(t) + t_fe = files_map.Location_To_File(t_loc) + comm = file_comments.Find_First_Comment(t_fe, t) + while comm != file_comments.No_Comment_Index: + txt += file_comments.Get_Comment(t_fe, comm) + '\n' + comm = file_comments.Get_Next_Comment(t_fe, comm) + newtext = buffer[:buf_len].decode(Document.encoding) + txt += "```vhdl\n" + newtext + "\n```" + res = {'contents': { 'kind': 'markdown', 'value': txt }} + prints.Free_Handle(hand) + return res def format_range(self, rng): first_line = rng["start"]["line"] + 1 @@ -206,10 +235,10 @@ class Document(object): return None if self._tree == nodes.Null_Iir: return None - hand = formatters.Allocate_Handle() + hand = prints.Allocate_Handle() formatters.Indent_String(self._tree, hand, first_line, last_line) - buffer = formatters.Get_C_String(hand) - buf_len = formatters.Get_Length(hand) + buffer = prints.Get_C_String(hand) + buf_len = prints.Get_Length(hand) newtext = buffer[:buf_len].decode(Document.encoding) res = [ { @@ -220,5 +249,5 @@ class Document(object): "newText": newtext, } ] - formatters.Free_Handle(hand) + prints.Free_Handle(hand) return res diff --git a/pyGHDL/lsp/lsp.py b/pyGHDL/lsp/lsp.py index 0b392ea84..32a64b3ea 100644 --- a/pyGHDL/lsp/lsp.py +++ b/pyGHDL/lsp/lsp.py @@ -126,7 +126,7 @@ class LanguageProtocolServer(object): log.error("Unexpected reply for %s", tid) return params = msg.get("params", None) - # Fix capitalization issues on windws. + # Fix capitalization issues on windows. if is_windows: normalize_rpc_file_uris(msg) fmethod = self.handler.dispatcher.get(method, None) diff --git a/pyGHDL/lsp/references.py b/pyGHDL/lsp/references.py index e99f473c2..2adde0fed 100644 --- a/pyGHDL/lsp/references.py +++ b/pyGHDL/lsp/references.py @@ -22,14 +22,14 @@ def find_def(n, loc): if n == nodes.Null_Iir: return None k = nodes.Get_Kind(n) - if k in [ + if k in ( nodes.Iir_Kind.Simple_Name, nodes.Iir_Kind.Character_Literal, nodes.Iir_Kind.Operator_Symbol, nodes.Iir_Kind.Selected_Name, nodes.Iir_Kind.Attribute_Name, nodes.Iir_Kind.Selected_Element, - ]: + ): n_loc = nodes.Get_Location(n) if loc >= n_loc: ident = nodes.Get_Identifier(n) @@ -48,6 +48,57 @@ def find_def(n, loc): return res unit = nodes.Get_Library_Unit(n) return find_def(unit, loc) + elif k in ( + nodes.Iir_Kind.Identity_Operator, + nodes.Iir_Kind.Negation_Operator, + nodes.Iir_Kind.Addition_Operator, + nodes.Iir_Kind.Substraction_Operator, + nodes.Iir_Kind.Multiplication_Operator, + nodes.Iir_Kind.Division_Operator, + nodes.Iir_Kind.Concatenation_Operator, + nodes.Iir_Kind.Equality_Operator, + nodes.Iir_Kind.Less_Than_Operator, + nodes.Iir_Kind.Greater_Than_Operator, + ): + n_loc = nodes.Get_Location(n) + if loc == n_loc: + return n + elif k in ( + nodes.Iir_Kind.Or_Operator, + nodes.Iir_Kind.Inequality_Operator, + nodes.Iir_Kind.Less_Than_Or_Equal_Operator, + nodes.Iir_Kind.Greater_Than_Or_Equal_Operator, + nodes.Iir_Kind.Condition_Operator, + nodes.Iir_Kind.Exponentiation_Operator, + ): + n_loc = nodes.Get_Location(n) + if n_loc <= loc <= n_loc + 1: + return n + elif k in ( + nodes.Iir_Kind.Absolute_Operator, + nodes.Iir_Kind.Not_Operator, + nodes.Iir_Kind.And_Operator, + nodes.Iir_Kind.Nor_Operator, + nodes.Iir_Kind.Xor_Operator, + nodes.Iir_Kind.Sll_Operator, + nodes.Iir_Kind.Sla_Operator, + nodes.Iir_Kind.Srl_Operator, + nodes.Iir_Kind.Sra_Operator, + nodes.Iir_Kind.Rol_Operator, + nodes.Iir_Kind.Ror_Operator, + nodes.Iir_Kind.Modulus_Operator, + nodes.Iir_Kind.Remainder_Operator, + ): + n_loc = nodes.Get_Location(n) + if n_loc <= loc <= n_loc + 2: + return n + elif k in ( + nodes.Iir_Kind.Nand_Operator, + nodes.Iir_Kind.Xnor_Operator, + ): + n_loc = nodes.Get_Location(n) + if n_loc <= loc <= n_loc + 3: + return n # This is *much* faster than using node_iter! for f in pyutils.fields_iter(n): @@ -84,17 +135,20 @@ def find_def(n, loc): return None - -def goto_definition(n, loc): - """Return the declaration (as a node) under :param loc: or None.""" +def find_node_by_loc(n, loc): + """Return the denoting node for :param loc: or None.""" ref = find_def(n, loc) log.debug("for loc %u found node %s", loc, ref) + return ref + +def find_definition_by_loc(n, loc): + """Return the declaration (as a node) under :param loc: or None.""" + ref = find_node_by_loc(n, loc) if ref is None: return None - log.debug( - "for loc %u id=%s", - loc, - name_table.Get_Name_Ptr(nodes.Get_Identifier(ref)), - ) - ent = nodes.Get_Named_Entity(ref) + k = nodes.Get_Kind(ref) + if k in nodes.Iir_Kinds.Denoting_Name or k == nodes.Iir_Kind.Selected_Element: + ent = nodes.Get_Named_Entity(ref) + else: + ent = nodes.Get_Implementation(ref) return None if ent == nodes.Null_Iir else ent diff --git a/pyGHDL/lsp/vhdl_ls.py b/pyGHDL/lsp/vhdl_ls.py index dea9542b9..01474f746 100644 --- a/pyGHDL/lsp/vhdl_ls.py +++ b/pyGHDL/lsp/vhdl_ls.py @@ -21,7 +21,7 @@ class VhdlLanguageServer(object): "textDocument/didChange": self.textDocument_didChange, "textDocument/didClose": self.textDocument_didClose, "textDocument/didSave": self.textDocument_didSave, - # 'textDocument/hover': self.hover, + "textDocument/hover": self.textDocument_hover, "textDocument/definition": self.textDocument_definition, "textDocument/documentSymbol": self.textDocument_documentSymbol, # 'textDocument/completion': self.completion, @@ -50,7 +50,7 @@ class VhdlLanguageServer(object): "change": lsp.TextDocumentSyncKind.INCREMENTAL, "save": {"includeText": True}, }, - "hoverProvider": False, + "hoverProvider": True, # 'completionProvider': False, # 'signatureHelpProvider': { # 'triggerCharacters': ['(', ','] @@ -125,6 +125,9 @@ class VhdlLanguageServer(object): self.lint(doc_uri) return res + def textDocument_hover(self, textDocument=None, position=None): + return self.workspace.hover(textDocument["uri"], position) + def m_workspace__did_change_configuration(self, _settings=None): for doc_uri in self.workspace.documents: self.lint(doc_uri) diff --git a/pyGHDL/lsp/workspace.py b/pyGHDL/lsp/workspace.py index 88200bc49..fe808c23c 100644 --- a/pyGHDL/lsp/workspace.py +++ b/pyGHDL/lsp/workspace.py @@ -46,6 +46,8 @@ class Workspace(object): errorout_memory.Install_Handler() flags.Flag_Elocations.value = True # flags.Verbose.value = True + # Gather comments + flags.Flag_Gather_Comments.value = True # We do analysis even in case of errors. parse.Flag_Parse_Parenthesis.value = True # Force analysis to get more feedback + navigation even in case @@ -393,12 +395,16 @@ class Workspace(object): if decl_loc is None: return None res = [decl_loc] + # If the declaration is a component, also add the entity. if nodes.Get_Kind(decl) == nodes.Iir_Kind.Component_Declaration: ent = libraries.Find_Entity_For_Component(nodes.Get_Identifier(decl)) if ent != nodes.Null_Iir: res.append(self.declaration_to_location(nodes.Get_Library_Unit(ent))) return res + def hover(self, doc_uri, position): + return self._docs[doc_uri].hover(position) + def x_show_all_files(self): res = [] for fe in range(1, files_map.Get_Last_Source_File_Entry() + 1): -- cgit v1.2.3