aboutsummaryrefslogtreecommitdiffstats
path: root/python/vhdl_langserver
diff options
context:
space:
mode:
Diffstat (limited to 'python/vhdl_langserver')
-rw-r--r--python/vhdl_langserver/__init__.py0
-rw-r--r--python/vhdl_langserver/document.py226
-rw-r--r--python/vhdl_langserver/lsp.py311
-rw-r--r--python/vhdl_langserver/lsptools.py41
-rw-r--r--python/vhdl_langserver/main.py130
-rw-r--r--python/vhdl_langserver/references.py100
-rw-r--r--python/vhdl_langserver/symbols.py177
-rw-r--r--python/vhdl_langserver/version.py1
-rw-r--r--python/vhdl_langserver/vhdl_ls.py150
-rw-r--r--python/vhdl_langserver/workspace.py499
10 files changed, 0 insertions, 1635 deletions
diff --git a/python/vhdl_langserver/__init__.py b/python/vhdl_langserver/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/python/vhdl_langserver/__init__.py
+++ /dev/null
diff --git a/python/vhdl_langserver/document.py b/python/vhdl_langserver/document.py
deleted file mode 100644
index 82d11fe56..000000000
--- a/python/vhdl_langserver/document.py
+++ /dev/null
@@ -1,226 +0,0 @@
-import ctypes
-import logging
-import os
-import libghdl.thin.name_table as name_table
-import libghdl.thin.files_map as files_map
-import libghdl.thin.files_map_editor as files_map_editor
-import libghdl.thin.libraries as libraries
-import libghdl.thin.vhdl.nodes as nodes
-import libghdl.thin.vhdl.sem_lib as sem_lib
-import libghdl.thin.vhdl.sem as sem
-import libghdl.thin.vhdl.formatters as formatters
-
-from . import symbols, references
-
-log = logging.getLogger(__name__)
-
-
-class Document(object):
- # The encoding used for the files.
- # Unfortunately this is not fully reliable. The client can read the
- # file using its own view of the encoding. It then pass the document
- # to the server using unicode(utf-8). Then the document is converted
- # back to bytes using this encoding. And we hope the result would be
- # the same as the file. Because VHDL uses the iso 8859-1 character
- # set, we use the same encoding. The client should also use 8859-1.
- encoding = "iso-8859-1"
-
- initial_gap_size = 4096
-
- def __init__(self, uri, sfe=None, version=None):
- self.uri = uri
- self.version = version
- self._fe = sfe
- self.gap_size = Document.initial_gap_size
- self._tree = nodes.Null_Iir
-
- @staticmethod
- def load(source, dirname, filename):
- # Write text to file buffer.
- src_bytes = source.encode(Document.encoding, "replace")
- src_len = len(src_bytes)
- buf_len = src_len + Document.initial_gap_size
- fileid = name_table.Get_Identifier(filename.encode("utf-8"))
- if os.path.isabs(filename):
- dirid = name_table.Null_Identifier
- else:
- dirid = name_table.Get_Identifier(dirname.encode("utf-8"))
- sfe = files_map.Reserve_Source_File(dirid, fileid, buf_len)
- files_map_editor.Fill_Text(sfe, ctypes.c_char_p(src_bytes), src_len)
- return sfe
-
- def reload(self, source):
- """Reload the source of a document. """
- src_bytes = source.encode(Document.encoding, "replace")
- files_map_editor.Fill_Text(self._fe, ctypes.c_char_p(src_bytes), len(src_bytes))
-
- def __str__(self):
- return str(self.uri)
-
- def apply_change(self, change):
- """Apply a change to the document."""
- text = change["text"]
- change_range = change.get("range")
-
- text_bytes = text.encode(Document.encoding, "replace")
-
- if not change_range:
- # The whole file has changed
- raise AssertionError
- # if len(text_bytes) < thin.Files_Map.Get_Buffer_Length(self._fe):
- # xxxx_replace
- # else:
- # xxxx_free
- # xxxx_allocate
- # return
-
- start_line = change_range["start"]["line"]
- start_col = change_range["start"]["character"]
- end_line = change_range["end"]["line"]
- end_col = change_range["end"]["character"]
-
- status = files_map_editor.Replace_Text(
- self._fe,
- start_line + 1,
- start_col,
- end_line + 1,
- end_col,
- ctypes.c_char_p(text_bytes),
- len(text_bytes),
- )
- if status:
- return
-
- # Failed to replace text.
- # Increase size
- self.gap_size *= 2
- fileid = files_map.Get_File_Name(self._fe)
- dirid = files_map.Get_Directory_Name(self._fe)
- buf_len = files_map.Get_File_Length(self._fe) + len(text_bytes) + self.gap_size
- files_map.Discard_Source_File(self._fe)
- new_sfe = files_map.Reserve_Source_File(dirid, fileid, buf_len)
- files_map_editor.Copy_Source_File(new_sfe, self._fe)
- files_map.Free_Source_File(self._fe)
- self._fe = new_sfe
- status = files_map_editor.Replace_Text(
- self._fe,
- start_line + 1,
- start_col,
- end_line + 1,
- end_col,
- ctypes.c_char_p(text_bytes),
- len(text_bytes),
- )
- assert status
-
- def check_document(self, text):
- log.debug("Checking document: %s", self.uri)
-
- text_bytes = text.encode(Document.encoding, "replace")
-
- files_map_editor.Check_Buffer_Content(
- self._fe, ctypes.c_char_p(text_bytes), len(text_bytes)
- )
-
- @staticmethod
- def add_to_library(tree):
- # Detach the chain of units.
- unit = nodes.Get_First_Design_Unit(tree)
- nodes.Set_First_Design_Unit(tree, nodes.Null_Iir)
- # FIXME: free the design file ?
- tree = nodes.Null_Iir
- # Analyze unit after unit.
- while unit != nodes.Null_Iir:
- # Pop the first unit.
- next_unit = nodes.Get_Chain(unit)
- nodes.Set_Chain(unit, nodes.Null_Iir)
- lib_unit = nodes.Get_Library_Unit(unit)
- if (
- lib_unit != nodes.Null_Iir
- and nodes.Get_Identifier(unit) != name_table.Null_Identifier
- ):
- # Put the unit (only if it has a library unit) in the library.
- libraries.Add_Design_Unit_Into_Library(unit, False)
- tree = nodes.Get_Design_File(unit)
- unit = next_unit
- return tree
-
- def parse_document(self):
- """Parse a document and put the units in the library"""
- assert self._tree == nodes.Null_Iir
- tree = sem_lib.Load_File(self._fe)
- if tree == nodes.Null_Iir:
- return
- self._tree = Document.add_to_library(tree)
- log.debug("add_to_library(%u) -> %u", tree, self._tree)
- if self._tree == nodes.Null_Iir:
- return
- nodes.Set_Design_File_Source(self._tree, self._fe)
-
- def compute_diags(self):
- log.debug("parse doc %d %s", self._fe, self.uri)
- self.parse_document()
- if self._tree == nodes.Null_Iir:
- # No units, nothing to add.
- return
- # Semantic analysis.
- unit = nodes.Get_First_Design_Unit(self._tree)
- while unit != nodes.Null_Iir:
- sem.Semantic(unit)
- nodes.Set_Date_State(unit, nodes.Date_State.Analyze)
- unit = nodes.Get_Chain(unit)
-
- def flatten_symbols(self, syms, parent):
- res = []
- for s in syms:
- s["location"] = {"uri": self.uri, "range": s["range"]}
- del s["range"]
- s.pop("detail", None)
- if parent is not None:
- s["containerName"] = parent
- res.append(s)
- children = s.pop("children", None)
- if children is not None:
- res.extend(self.flatten_symbols(children, s))
- return res
-
- def document_symbols(self):
- log.debug("document_symbols")
- if self._tree == nodes.Null_Iir:
- return []
- syms = symbols.get_symbols_chain(
- self._fe, nodes.Get_First_Design_Unit(self._tree)
- )
- return self.flatten_symbols(syms, None)
-
- def position_to_location(self, position):
- pos = files_map.File_Line_To_Position(self._fe, position["line"] + 1)
- return files_map.File_Pos_To_Location(self._fe, pos) + position["character"]
-
- def goto_definition(self, position):
- loc = self.position_to_location(position)
- return references.goto_definition(self._tree, loc)
-
- def format_range(self, rng):
- first_line = rng["start"]["line"] + 1
- last_line = rng["end"]["line"] + (1 if rng["end"]["character"] != 0 else 0)
- if last_line < first_line:
- return None
- if self._tree == nodes.Null_Iir:
- return None
- hand = formatters.Allocate_Handle()
- formatters.Indent_String(self._tree, hand, first_line, last_line)
- buffer = formatters.Get_C_String(hand)
- buf_len = formatters.Get_Length(hand)
- newtext = buffer[:buf_len].decode(Document.encoding)
- res = [
- {
- "range": {
- "start": {"line": first_line - 1, "character": 0},
- "end": {"line": last_line, "character": 0},
- },
- "newText": newtext,
- }
- ]
- formatters.Free_Handle(hand)
- return res
diff --git a/python/vhdl_langserver/lsp.py b/python/vhdl_langserver/lsp.py
deleted file mode 100644
index 60f32be76..000000000
--- a/python/vhdl_langserver/lsp.py
+++ /dev/null
@@ -1,311 +0,0 @@
-import os
-import logging
-import json
-import attr
-from attr.validators import instance_of
-
-try:
- from urllib.parse import unquote, quote
-except ImportError:
- from urllib2 import quote
- from urlparse import unquote
-
-log = logging.getLogger("ghdl-ls")
-
-
-class ProtocolError(Exception):
- pass
-
-
-class LSPConn:
- def __init__(self, reader, writer):
- self.reader = reader
- self.writer = writer
-
- def readline(self):
- data = self.reader.readline()
- return data.decode("utf-8")
-
- def read(self, size):
- data = self.reader.read(size)
- return data.decode("utf-8")
-
- def write(self, out):
- self.writer.write(out.encode())
- self.writer.flush()
-
-
-def path_from_uri(uri):
- # Convert file uri to path (strip html like head part)
- if not uri.startswith("file://"):
- return uri
- if os.name == "nt":
- _, path = uri.split("file:///", 1)
- else:
- _, path = uri.split("file://", 1)
- return os.path.normpath(unquote(path))
-
-
-def path_to_uri(path):
- # Convert path to file uri (add html like head part)
- if os.name == "nt":
- return "file:///" + quote(path.replace("\\", "/"))
- else:
- return "file://" + quote(path)
-
-
-class LanguageProtocolServer(object):
- def __init__(self, handler, conn):
- self.conn = conn
- self.handler = handler
- if handler is not None:
- handler.set_lsp(self)
- self.running = True
- self._next_id = 0
-
- def read_request(self):
- headers = {}
- while True:
- # Read a line
- line = self.conn.readline()
- # Return on EOF.
- if not line:
- return None
- if line[-2:] != "\r\n":
- raise ProtocolError("invalid end of line in header")
- line = line[:-2]
- if not line:
- # End of headers.
- log.debug("Headers: %r", headers)
- length = headers.get("Content-Length", None)
- if length is not None:
- body = self.conn.read(int(length))
- return body
- else:
- raise ProtocolError("missing Content-Length in header")
- else:
- key, value = line.split(": ", 1)
- headers[key] = value
-
- def run(self):
- while self.running:
- body = self.read_request()
- if body is None:
- # EOF
- break
-
- # Text to JSON
- msg = json.loads(body)
- log.debug("Read msg: %s", msg)
-
- reply = self.handle(msg)
- if reply is not None:
- self.write_output(reply)
-
- def handle(self, msg):
- if msg.get("jsonrpc", None) != "2.0":
- raise ProtocolError("invalid jsonrpc version")
- tid = msg.get("id", None)
- method = msg.get("method", None)
- if method is None:
- # This is a reply.
- log.error("Unexpected reply for %s", tid)
- return
- params = msg.get("params", None)
- fmethod = self.handler.dispatcher.get(method, None)
- if fmethod:
- if params is None:
- params = {}
- try:
- response = fmethod(**params)
- except Exception as e:
- log.exception(
- "Caught exception while handling %s with params %s:", method, params
- )
- self.show_message(
- MessageType.Error,
- (
- "Caught exception while handling {}, "
- + "see VHDL language server output for details."
- ).format(method),
- )
- response = None
- if tid is None:
- # If this was just a notification, discard it
- return None
- log.debug("Response: %s", response)
- rbody = {
- "jsonrpc": "2.0",
- "id": tid,
- "result": response,
- }
- else:
- # Unknown method.
- log.error("Unknown method %s", method)
- # If this was just a notification, discard it
- if tid is None:
- return None
- # Otherwise create an error.
- rbody = {
- "jsonrpc": "2.0",
- "id": tid,
- "error": {
- "code": JSONErrorCodes.MethodNotFound,
- "message": "unknown method {}".format(method),
- },
- }
- return rbody
-
- def write_output(self, body):
- output = json.dumps(body, separators=(",", ":"))
- self.conn.write("Content-Length: {}\r\n".format(len(output)))
- self.conn.write("\r\n")
- self.conn.write(output)
-
- def notify(self, method, params):
- """Send a notification"""
- body = {
- "jsonrpc": "2.0",
- "method": method,
- "params": params,
- }
- self.write_output(body)
-
- def send_request(self, method, params):
- """Send a request"""
- self._next_id += 1
- body = {
- "jsonrpc": "2.0",
- "id": self._next_id,
- "method": method,
- "params": params,
- }
- self.write_output(body)
-
- def shutdown(self):
- """Prepare to shutdown the server"""
- self.running = False
-
- def show_message(self, typ, message):
- self.notify("window/showMessage", {"type": typ, "message": message})
-
- def configuration(self, items):
- return self.send_request("workspace/configuration", {"items": items})
-
-
-# ----------------------------------------------------------------------
-# Standard defines and object types
-#
-
-
-class JSONErrorCodes(object):
- # Defined by JSON RPC
- ParseError = -32700
- InvalidRequest = -32600
- MethodNotFound = -32601
- InvalidParams = -32602
- InternalError = -32603
- serverErrorStart = -32099
- serverErrorEnd = -32000
- ServerNotInitialized = -32002
- UnknownErrorCode = -32001
-
- # Defined by the protocol.
- RequestCancelled = -32800
- ContentModified = -32801
-
-
-class CompletionKind(object):
- Text = 1
- Method = 2
- Function = 3
- Constructor = 4
- Field = 5
- Variable = 6
- Class = 7
- Interface = 8
- Module = 9
- Property = 10
- Unit = 11
- Value = 12
- Enum = 13
- Keyword = 14
- Snippet = 15
- Color = 16
- File = 17
- Reference = 18
-
-
-class DiagnosticSeverity(object):
- Error = 1
- Warning = 2
- Information = 3
- Hint = 4
-
-
-class TextDocumentSyncKind(object):
- NONE = (0,)
- FULL = 1
- INCREMENTAL = 2
-
-
-class MessageType(object):
- Error = 1
- Warning = 2
- Info = 3
- Log = 4
-
-
-class SymbolKind(object):
- File = 1
- Module = 2
- Namespace = 3
- Package = 4
- Class = 5
- Method = 6
- Property = 7
- Field = 8
- Constructor = 9
- Enum = 10
- Interface = 11
- Function = 12
- Variable = 13
- Constant = 14
- String = 15
- Number = 16
- Boolean = 17
- Array = 18
-
-
-@attr.s
-class HoverInfo(object):
- language = attr.ib()
- value = attr.ib()
-
-
-@attr.s
-class Completion(object):
- label = attr.ib()
- kind = attr.ib()
- detail = attr.ib()
- documentation = attr.ib()
-
-
-@attr.s
-class Position(object):
- line = attr.ib()
- character = attr.ib()
-
-
-@attr.s
-class Range(object):
- start = attr.ib(validator=instance_of(Position))
- end = attr.ib(validator=instance_of(Position))
-
-
-@attr.s
-class Diagnostic(object):
- range = attr.ib(validator=instance_of(Range))
- severity = attr.ib()
- source = attr.ib()
- message = attr.ib()
diff --git a/python/vhdl_langserver/lsptools.py b/python/vhdl_langserver/lsptools.py
deleted file mode 100644
index 648f0a8c0..000000000
--- a/python/vhdl_langserver/lsptools.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import sys
-import argparse
-import json
-from . import lsp
-
-
-def lsp2json():
- "Utility that transforms lsp log file to a JSON list"
- conn = lsp.LSPConn(sys.stdin.buffer, sys.stdout.buffer)
- ls = lsp.LanguageProtocolServer(None, conn)
- res = []
- while True:
- req = ls.read_request()
- if req is None:
- break
- res.append(json.loads(req))
- print(json.dumps(res, indent=2))
-
-
-def json2lsp():
- "Utility that transform a JSON list to an lsp file"
- res = json.load(sys.stdin)
- conn = lsp.LSPConn(sys.stdin.buffer, sys.stdout.buffer)
- ls = lsp.LanguageProtocolServer(None, conn)
- for req in res:
- ls.write_output(req)
-
-
-def main():
- parser = argparse.ArgumentParser()
- subparsers = parser.add_subparsers(help="sub-command help")
- parser_l2j = subparsers.add_parser("lsp2json", help="convert lsp dump to JSON")
- parser_l2j.set_defaults(func=lsp2json)
- parser_j2l = subparsers.add_parser("json2lsp", help="convert JSON to lsp dump")
- parser_j2l.set_defaults(func=json2lsp)
- args = parser.parse_args()
- args.func()
-
-
-if __name__ == "__main__":
- main()
diff --git a/python/vhdl_langserver/main.py b/python/vhdl_langserver/main.py
deleted file mode 100644
index 5fe0e0549..000000000
--- a/python/vhdl_langserver/main.py
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/usr/bin/env python
-from __future__ import absolute_import
-
-import argparse
-import logging
-import sys
-import os
-
-import libghdl
-import libghdl.thin.errorout_console
-
-from . import version
-from . import lsp
-from . import vhdl_ls
-
-logger = logging.getLogger("ghdl-ls")
-
-
-class LSPConnTrace(object):
- """Wrapper class to save in and out packets"""
-
- def __init__(self, basename, conn):
- self.conn = conn
- self.trace_in = open(basename + ".in", "w")
- self.trace_out = open(basename + ".out", "w")
-
- def readline(self):
- res = self.conn.readline()
- self.trace_in.write(res)
- return res
-
- def read(self, size):
- res = self.conn.read(size)
- self.trace_in.write(res)
- self.trace_in.flush()
- return res
-
- def write(self, out):
- self.conn.write(out)
- self.trace_out.write(out)
- self.trace_out.flush()
-
-
-def rotate_log_files(basename, num):
- for i in range(num, 0, -1):
- oldfile = "{}.{}".format(basename, i - 1)
- if os.path.isfile(oldfile):
- os.rename(oldfile, "{}.{}".format(basename, i))
- if os.path.isfile(basename):
- os.rename(basename, "{}.0".format(basename))
-
-
-def main():
- parser = argparse.ArgumentParser(description="VHDL Language Protocol Server")
- parser.add_argument(
- "--version", "-V", action="version", version="%(prog)s " + version.__version__
- )
- parser.add_argument(
- "--verbose", "-v", action="count", default=0, help="Show debug output"
- )
- parser.add_argument(
- "--log-file", help="Redirect logs to the given file instead of stderr"
- )
- parser.add_argument("--trace-file", help="Save rpc data to FILE.in and FILE.out")
- parser.add_argument("--input", "-i", help="Read request from file")
- parser.add_argument(
- "--disp-config",
- action="store_true",
- help="Disp installation configuration and exit",
- )
-
- args = parser.parse_args()
-
- if args.disp_config:
- libghdl.thin.errorout_console.Install_Handler()
- libghdl.disp_config()
- return
-
- # Setup logging
- if args.verbose >= 2:
- loglevel = logging.DEBUG
- elif args.verbose >= 1:
- loglevel = logging.INFO
- else:
- loglevel = logging.ERROR
-
- if args.log_file:
- rotate_log_files(args.log_file, 5)
- logstream = open(args.log_file, "w")
- else:
- logstream = sys.stderr
- logging.basicConfig(
- format="%(asctime)-15s [%(levelname)s] %(message)s",
- stream=logstream,
- level=loglevel,
- )
-
- if args.verbose != 0:
- sys.stderr.write("Args: {}\n".format(sys.argv))
- sys.stderr.write("Current directory: {}\n".format(os.getcwd()))
-
- logger.info("Args: %s", sys.argv)
- logger.info("Current directory is %s", os.getcwd())
-
- # Connection
- instream = sys.stdin.buffer
- if args.input is not None:
- instream = open(args.input, "rb")
-
- conn = lsp.LSPConn(instream, sys.stdout.buffer)
-
- trace_file = args.trace_file
- if trace_file is None:
- trace_file = os.environ.get("GHDL_LS_TRACE")
- if trace_file is not None:
- if args.input is None:
- rotate_log_files(trace_file + ".in", 5)
- rotate_log_files(trace_file + ".out", 5)
- conn = LSPConnTrace(trace_file, conn)
- else:
- logger.info("Traces disabled when -i/--input")
-
- handler = vhdl_ls.VhdlLanguageServer()
-
- try:
- server = lsp.LanguageProtocolServer(handler, conn)
- server.run()
- except Exception:
- logger.exception("Uncaught error")
- sys.exit(1)
diff --git a/python/vhdl_langserver/references.py b/python/vhdl_langserver/references.py
deleted file mode 100644
index f716548eb..000000000
--- a/python/vhdl_langserver/references.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import logging
-import libghdl.thin.vhdl.nodes as nodes
-import libghdl.thin.vhdl.nodes_meta as nodes_meta
-import libghdl.thin.vhdl.pyutils as pyutils
-import libghdl.thin.name_table as name_table
-
-log = logging.getLogger(__name__)
-
-
-def find_def_chain(first, loc):
- n1 = first
- while n1 != nodes.Null_Iir:
- res = find_def(n1, loc)
- if res is not None:
- return res
- n1 = nodes.Get_Chain(n1)
- return None
-
-
-def find_def(n, loc):
- "Return the node at location :param loc:, or None if not under :param n:"
- if n == nodes.Null_Iir:
- return None
- k = nodes.Get_Kind(n)
- 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)
- id_len = name_table.Get_Name_Length(ident)
- if loc < n_loc + id_len:
- return n
- if k == nodes.Iir_Kind.Simple_Name:
- return None
- elif k == nodes.Iir_Kind.Design_File:
- return find_def_chain(nodes.Get_First_Design_Unit(n), loc)
- elif k == nodes.Iir_Kind.Design_Unit:
- # if loc > elocations.Get_End_Location(unit):
- # return None
- res = find_def_chain(nodes.Get_Context_Items(n), loc)
- if res is not None:
- return res
- unit = nodes.Get_Library_Unit(n)
- return find_def(unit, loc)
-
- # This is *much* faster than using node_iter!
- for f in pyutils.fields_iter(n):
- typ = nodes_meta.get_field_type(f)
- if typ == nodes_meta.types.Iir:
- attr = nodes_meta.get_field_attribute(f)
- if attr == nodes_meta.Attr.ANone:
- res = find_def(nodes_meta.Get_Iir(n, f), loc)
- if res is not None:
- return res
- elif attr == nodes_meta.Attr.Chain:
- res = find_def_chain(nodes_meta.Get_Iir(n, f), loc)
- if res is not None:
- return res
- elif attr == nodes_meta.Attr.Maybe_Ref:
- if not nodes.Get_Is_Ref(n, f):
- res = find_def(nodes_meta.Get_Iir(n, f), loc)
- if res is not None:
- return res
- elif typ == nodes_meta.types.Iir_List:
- attr = nodes_meta.get_field_attribute(f)
- if attr == nodes_meta.Attr.ANone:
- for n1 in pyutils.list_iter(nodes_meta.Get_Iir_List(n, f)):
- res = find_def(n1, loc)
- if res is not None:
- return res
- elif typ == nodes_meta.types.Iir_Flist:
- attr = nodes_meta.get_field_attribute(f)
- if attr == nodes_meta.Attr.ANone:
- for n1 in pyutils.flist_iter(nodes_meta.Get_Iir_Flist(n, f)):
- res = find_def(n1, loc)
- if res is not None:
- return res
-
- return None
-
-
-def goto_definition(n, loc):
- "Return the declaration (as a node) under :param loc: or None"
- ref = find_def(n, loc)
- log.debug("for loc %u found node %s", loc, ref)
- if ref is None:
- return None
- log.debug(
- "for loc %u id=%s",
- loc,
- name_table.Get_Name_Ptr(nodes.Get_Identifier(ref)).decode("utf-8"),
- )
- ent = nodes.Get_Named_Entity(ref)
- return None if ent == nodes.Null_Iir else ent
diff --git a/python/vhdl_langserver/symbols.py b/python/vhdl_langserver/symbols.py
deleted file mode 100644
index d0bd269de..000000000
--- a/python/vhdl_langserver/symbols.py
+++ /dev/null
@@ -1,177 +0,0 @@
-import libghdl.thin.name_table as name_table
-import libghdl.thin.files_map as files_map
-import libghdl.thin.vhdl.pyutils as pyutils
-import libghdl.thin.vhdl.nodes as nodes
-import libghdl.thin.vhdl.nodes_meta as nodes_meta
-import libghdl.thin.vhdl.elocations as elocations
-
-from . import lsp
-
-SYMBOLS_MAP = {
- nodes.Iir_Kind.Package_Declaration: {
- "kind": lsp.SymbolKind.Package,
- "detail": "(declaration)",
- },
- nodes.Iir_Kind.Package_Body: {"kind": lsp.SymbolKind.Package, "detail": "(body)"},
- nodes.Iir_Kind.Entity_Declaration: {"kind": lsp.SymbolKind.Module},
- nodes.Iir_Kind.Architecture_Body: {"kind": lsp.SymbolKind.Module},
- nodes.Iir_Kind.Configuration_Declaration: {"kind": lsp.SymbolKind.Module},
- nodes.Iir_Kind.Package_Instantiation_Declaration: {"kind": lsp.SymbolKind.Module},
- nodes.Iir_Kind.Component_Declaration: {"kind": lsp.SymbolKind.Module},
- nodes.Iir_Kind.Context_Declaration: {"kind": lsp.SymbolKind.Module},
- nodes.Iir_Kind.Use_Clause: {"kind": None},
- nodes.Iir_Kind.Library_Clause: {"kind": None},
- nodes.Iir_Kind.Procedure_Declaration: {"kind": lsp.SymbolKind.Function},
- nodes.Iir_Kind.Function_Declaration: {"kind": lsp.SymbolKind.Function},
- nodes.Iir_Kind.Interface_Procedure_Declaration: {"kind": lsp.SymbolKind.Function},
- nodes.Iir_Kind.Interface_Function_Declaration: {"kind": lsp.SymbolKind.Function},
- nodes.Iir_Kind.Procedure_Body: {
- "kind": lsp.SymbolKind.Function,
- "detail": "(body)",
- },
- nodes.Iir_Kind.Function_Body: {"kind": lsp.SymbolKind.Function, "detail": "(body)"},
- nodes.Iir_Kind.Type_Declaration: {"kind": lsp.SymbolKind.Constructor},
- nodes.Iir_Kind.Subtype_Declaration: {"kind": lsp.SymbolKind.Constructor},
- nodes.Iir_Kind.Attribute_Declaration: {"kind": lsp.SymbolKind.Property},
- nodes.Iir_Kind.Attribute_Specification: {"kind": None},
- nodes.Iir_Kind.Disconnection_Specification: {"kind": None},
- nodes.Iir_Kind.Anonymous_Type_Declaration: {"kind": None},
- nodes.Iir_Kind.Variable_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Constant_Declaration: {"kind": lsp.SymbolKind.Constant},
- nodes.Iir_Kind.Signal_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Signal_Attribute_Declaration: {"kind": None},
- nodes.Iir_Kind.File_Declaration: {"kind": lsp.SymbolKind.File},
- nodes.Iir_Kind.Interface_Variable_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Interface_Constant_Declaration: {"kind": lsp.SymbolKind.Constant},
- nodes.Iir_Kind.Interface_Signal_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Interface_File_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.File_Declaration: {"kind": lsp.SymbolKind.File},
- nodes.Iir_Kind.Object_Alias_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Non_Object_Alias_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Protected_Type_Body: {"kind": lsp.SymbolKind.Class},
- nodes.Iir_Kind.Group_Template_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Group_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Concurrent_Simple_Signal_Assignment: {"kind": None},
- nodes.Iir_Kind.Concurrent_Conditional_Signal_Assignment: {"kind": None},
- nodes.Iir_Kind.Concurrent_Selected_Signal_Assignment: {"kind": None},
- nodes.Iir_Kind.Concurrent_Procedure_Call_Statement: {"kind": None},
- nodes.Iir_Kind.Concurrent_Assertion_Statement: {"kind": None},
- nodes.Iir_Kind.Component_Instantiation_Statement: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Block_Statement: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.If_Generate_Statement: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.For_Generate_Statement: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Case_Generate_Statement: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Sensitized_Process_Statement: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Process_Statement: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Psl_Assert_Directive: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Psl_Assume_Directive: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Psl_Cover_Directive: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Psl_Restrict_Directive: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Psl_Endpoint_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Psl_Declaration: {"kind": lsp.SymbolKind.Variable},
- nodes.Iir_Kind.Psl_Assert_Directive: {"kind": lsp.SymbolKind.Method},
- nodes.Iir_Kind.Configuration_Specification: {"kind": None},
-}
-
-
-def location_to_position(fe, loc):
- assert loc != files_map.No_Location
- line = files_map.Location_File_To_Line(loc, fe)
- off = files_map.Location_File_Line_To_Offset(loc, fe, line)
- return {"line": line - 1, "character": off}
-
-
-def get_symbols_chain(fe, n):
- res = [get_symbols(fe, el) for el in pyutils.chain_iter(n)]
- return [e for e in res if e is not None]
-
-
-def get_symbols(fe, n):
- if n == nodes.Null_Iir:
- return None
- k = nodes.Get_Kind(n)
- if k == nodes.Iir_Kind.Design_Unit:
- return get_symbols(fe, nodes.Get_Library_Unit(n))
- m = SYMBOLS_MAP.get(k, None)
- if m is None:
- raise AssertionError("get_symbol: unhandled {}".format(pyutils.kind_image(k)))
- kind = m["kind"]
- if kind is None:
- return None
- if k in [nodes.Iir_Kind.Procedure_Declaration, nodes.Iir_Kind.Function_Declaration]:
- # Discard implicit declarations.
- if nodes.Get_Implicit_Definition(n) < nodes.Iir_Predefined.PNone:
- return None
- if nodes.Get_Has_Body(n):
- # Use the body instead.
- # FIXME: but get interface from the spec!
- return None
- res = {"kind": kind}
- detail = m.get("detail")
- if detail is not None:
- res["detail"] = detail
- # Get the name
- if k in [nodes.Iir_Kind.Function_Body, nodes.Iir_Kind.Procedure_Body]:
- nid = nodes.Get_Identifier(nodes.Get_Subprogram_Specification(n))
- else:
- nid = nodes.Get_Identifier(n)
- if nid == name_table.Null_Identifier:
- name = None
- else:
- name = pyutils.name_image(nid)
- # Get the range. Use elocations when possible.
- if k in (
- nodes.Iir_Kind.Architecture_Body,
- nodes.Iir_Kind.Entity_Declaration,
- nodes.Iir_Kind.Package_Declaration,
- nodes.Iir_Kind.Package_Body,
- nodes.Iir_Kind.Component_Declaration,
- nodes.Iir_Kind.Process_Statement,
- nodes.Iir_Kind.Sensitized_Process_Statement,
- nodes.Iir_Kind.If_Generate_Statement,
- nodes.Iir_Kind.For_Generate_Statement,
- ):
- start_loc = elocations.Get_Start_Location(n)
- end_loc = elocations.Get_End_Location(n)
- if end_loc == files_map.No_Location:
- # Can happen in case of parse error
- end_loc = start_loc
- else:
- start_loc = nodes.Get_Location(n)
- end_loc = start_loc + name_table.Get_Name_Length(nid)
- res["range"] = {
- "start": location_to_position(fe, start_loc),
- "end": location_to_position(fe, end_loc),
- }
-
- # Gather children.
- # FIXME: should we use a list of fields to inspect ?
- children = []
- # if nodes_meta.Has_Generic_Chain(k):
- # children.extend(get_symbols_chain(fe, nodes.Get_Generic_Chain(n)))
- # if nodes_meta.Has_Port_Chain(k):
- # children.extend(get_symbols_chain(fe, nodes.Get_Port_Chain(n)))
- # if nodes_meta.Has_Interface_Declaration_Chain(k):
- # children.extend(get_symbols_chain(fe, nodes.Get_Interface_Declaration_Chain(n)))
- if k in (nodes.Iir_Kind.Package_Declaration, nodes.Iir_Kind.Package_Body):
- children.extend(get_symbols_chain(fe, nodes.Get_Declaration_Chain(n)))
- if nodes_meta.Has_Concurrent_Statement_Chain(k):
- children.extend(get_symbols_chain(fe, nodes.Get_Concurrent_Statement_Chain(n)))
- if nodes_meta.Has_Generate_Statement_Body(k):
- children.extend(
- get_symbols_chain(
- fe,
- nodes.Get_Concurrent_Statement_Chain(
- nodes.Get_Generate_Statement_Body(n)
- ),
- )
- )
-
- if children:
- res["children"] = children
- else:
- # Discard anonymous symbols without children.
- if name is None:
- return None
- res["name"] = name if name is not None else "<anon>"
- return res
diff --git a/python/vhdl_langserver/version.py b/python/vhdl_langserver/version.py
deleted file mode 100644
index 4b0d124d5..000000000
--- a/python/vhdl_langserver/version.py
+++ /dev/null
@@ -1 +0,0 @@
-__version__ = "0.1dev"
diff --git a/python/vhdl_langserver/vhdl_ls.py b/python/vhdl_langserver/vhdl_ls.py
deleted file mode 100644
index 61c4aed23..000000000
--- a/python/vhdl_langserver/vhdl_ls.py
+++ /dev/null
@@ -1,150 +0,0 @@
-import logging
-
-from . import lsp
-from .workspace import Workspace
-
-log = logging.getLogger(__name__)
-
-
-class VhdlLanguageServer(object):
- def __init__(self):
- self.workspace = None
- self.lsp = None
- self._shutdown = False
- self.dispatcher = {
- "initialize": self.initialize,
- "initialized": self.initialized,
- "shutdown": self.shutdown,
- "$/setTraceNotification": self.setTraceNotification,
- "textDocument/didOpen": self.textDocument_didOpen,
- "textDocument/didChange": self.textDocument_didChange,
- "textDocument/didClose": self.textDocument_didClose,
- "textDocument/didSave": self.textDocument_didSave,
- # 'textDocument/hover': self.hover,
- "textDocument/definition": self.textDocument_definition,
- "textDocument/documentSymbol": self.textDocument_documentSymbol,
- # 'textDocument/completion': self.completion,
- "textDocument/rangeFormatting": self.textDocument_rangeFormatting,
- "workspace/xShowAllFiles": self.workspace_xShowAllFiles,
- "workspace/xGetAllEntities": self.workspace_xGetAllEntities,
- "workspace/xGetEntityInterface": self.workspace_xGetEntityInterface,
- }
-
- def set_lsp(self, server):
- self.lsp = server
-
- def shutdown(self):
- self.lsp.shutdown()
-
- def setTraceNotification(self, value):
- pass
-
- def capabilities(self):
- server_capabilities = {
- "textDocumentSync": {
- "openClose": True,
- "change": lsp.TextDocumentSyncKind.INCREMENTAL,
- "save": {"includeText": True},
- },
- "hoverProvider": False,
- # 'completionProvider': False,
- # 'signatureHelpProvider': {
- # 'triggerCharacters': ['(', ',']
- # },
- "definitionProvider": True,
- "referencesProvider": False,
- "documentHighlightProvider": False,
- "documentSymbolProvider": True,
- "codeActionProvider": False,
- "documentFormattingProvider": False,
- "documentRangeFormattingProvider": True,
- "renameProvider": False,
- }
- return server_capabilities
-
- def initialize(
- self,
- processId,
- rootPath,
- capabilities,
- rootUri=None,
- initializationOptions=None,
- **_
- ):
- log.debug(
- "Language server initialized with %s %s %s %s",
- processId,
- rootUri,
- rootPath,
- initializationOptions,
- )
- if rootUri is None:
- rootUri = lsp.path_to_uri(rootPath) if rootPath is not None else ""
- self.workspace = Workspace(rootUri, self.lsp)
-
- # Get our capabilities
- return {"capabilities": self.capabilities()}
-
- def initialized(self):
- # Event when the client is fully initialized.
- return None
-
- def textDocument_didOpen(self, textDocument=None):
- doc_uri = textDocument["uri"]
- self.workspace.put_document(
- doc_uri, textDocument["text"], version=textDocument.get("version")
- )
- self.lint(doc_uri)
-
- def textDocument_didChange(self, textDocument=None, contentChanges=None, **_kwargs):
- doc_uri = textDocument["uri"]
- new_version = textDocument.get("version")
- self.workspace.apply_changes(doc_uri, contentChanges, new_version)
-
- def lint(self, doc_uri):
- self.workspace.lint(doc_uri)
-
- def textDocument_didClose(self, textDocument=None, **_kwargs):
- self.workspace.rm_document(textDocument["uri"])
-
- def textDocument_didSave(self, textDocument=None, text=None, **_kwargs):
- if text is not None:
- # Sanity check: check we have the same content for the document.
- self.workspace.check_document(textDocument["uri"], text)
- else:
- log.debug("did save - no text")
- self.lint(textDocument["uri"])
-
- def textDocument_definition(self, textDocument=None, position=None):
- return self.workspace.goto_definition(textDocument["uri"], position)
-
- def textDocument_documentSymbol(self, textDocument=None):
- doc = self.workspace.get_or_create_document(textDocument["uri"])
- return doc.document_symbols()
-
- def textDocument_rangeFormatting(self, textDocument=None, range=None, options=None):
- doc_uri = textDocument["uri"]
- doc = self.workspace.get_document(doc_uri)
- assert doc is not None, "Try to format a non-loaded document"
- res = doc.format_range(range)
- if res is not None:
- self.lint(doc_uri)
- return res
-
- def m_workspace__did_change_configuration(self, _settings=None):
- for doc_uri in self.workspace.documents:
- self.lint(doc_uri)
-
- def m_workspace__did_change_watched_files(self, **_kwargs):
- # Externally changed files may result in changed diagnostics
- for doc_uri in self.workspace.documents:
- self.lint(doc_uri)
-
- def workspace_xShowAllFiles(self):
- return self.workspace.x_show_all_files()
-
- def workspace_xGetAllEntities(self):
- return self.workspace.x_get_all_entities()
-
- def workspace_xGetEntityInterface(self, library, name):
- return self.workspace.x_get_entity_interface(library, name)
diff --git a/python/vhdl_langserver/workspace.py b/python/vhdl_langserver/workspace.py
deleted file mode 100644
index 9d61225ee..000000000
--- a/python/vhdl_langserver/workspace.py
+++ /dev/null
@@ -1,499 +0,0 @@
-import logging
-import os
-import json
-from ctypes import byref
-import libghdl
-import libghdl.thin.errorout_memory as errorout_memory
-import libghdl.thin.flags
-import libghdl.thin.errorout as errorout
-import libghdl.thin.files_map as files_map
-import libghdl.thin.libraries as libraries
-import libghdl.thin.name_table as name_table
-import libghdl.thin.vhdl.nodes as nodes
-import libghdl.thin.vhdl.lists as lists
-import libghdl.thin.vhdl.std_package as std_package
-import libghdl.thin.vhdl.parse
-import libghdl.thin.vhdl.pyutils as pyutils
-import libghdl.thin.vhdl.sem_lib as sem_lib
-
-from . import lsp
-from . import document, symbols
-
-log = logging.getLogger(__name__)
-
-
-class ProjectError(Exception):
- "Exception raised in case of unrecoverable error in the project file."
-
- def __init__(self, msg):
- super().__init__()
- self.msg = msg
-
-
-class Workspace(object):
- def __init__(self, root_uri, server):
- self._root_uri = root_uri
- self._server = server
- self._root_path = lsp.path_from_uri(self._root_uri)
- self._docs = {} # uri -> doc
- self._fe_map = {} # fe -> doc
- self._prj = {}
- self._last_linted_doc = None
- errorout_memory.Install_Handler()
- libghdl.thin.flags.Flag_Elocations.value = True
- # thin.Flags.Verbose.value = True
- # We do analysis even in case of errors.
- libghdl.thin.vhdl.parse.Flag_Parse_Parenthesis.value = True
- # Force analysis to get more feedback + navigation even in case
- # of errors.
- libghdl.thin.flags.Flag_Force_Analysis.value = True
- # Do not consider analysis order issues.
- libghdl.thin.flags.Flag_Elaborate_With_Outdated.value = True
- libghdl.thin.errorout.Enable_Warning(errorout.Msgid.Warnid_Unused, True)
- self.read_project()
- self.set_options_from_project()
- libghdl.analyze_init()
- self._diags_set = set() # Documents with at least one diagnostic.
- self.read_files_from_project()
- self.gather_diagnostics(None)
-
- @property
- def documents(self):
- return self._docs
-
- @property
- def root_path(self):
- return self._root_path
-
- @property
- def root_uri(self):
- return self._root_uri
-
- def _create_document(self, doc_uri, sfe, version=None):
- """Create a document and put it in this workspace."""
- doc = document.Document(doc_uri, sfe, version)
- self._docs[doc_uri] = doc
- self._fe_map[sfe] = doc
- return doc
-
- def create_document_from_sfe(self, sfe, abspath):
- # A filename has been given without a corresponding document.
- # Create the document.
- # Common case: an error message was reported in a non-open document.
- # Create a document so that it could be reported to the client.
- doc_uri = lsp.path_to_uri(os.path.normpath(abspath))
- return self._create_document(doc_uri, sfe)
-
- def create_document_from_uri(self, doc_uri, source=None, version=None):
- # A document is referenced by an uri but not known. Load it.
- # We assume the path is correct.
- path = lsp.path_from_uri(doc_uri)
- if source is None:
- source = open(path).read()
- sfe = document.Document.load(
- source, os.path.dirname(path), os.path.basename(path)
- )
- return self._create_document(doc_uri, sfe)
-
- def get_or_create_document(self, doc_uri):
- res = self.get_document(doc_uri)
- if res is not None:
- return res
- res = self.create_document_from_uri(doc_uri)
- res.parse_document()
- return res
-
- def get_document(self, doc_uri):
- """Get a document from :param doc_uri: Note that the document may not exist,
- and this function may return None."""
- return self._docs.get(doc_uri)
-
- def put_document(self, doc_uri, source, version=None):
- doc = self.get_document(doc_uri)
- if doc is None:
- doc = self.create_document_from_uri(doc_uri, source=source, version=version)
- else:
- # The document may already be present (loaded from a project)
- # In that case, overwrite it as the client may have a more
- # recent version.
- doc.reload(source)
- return doc
-
- def sfe_to_document(self, sfe):
- """Get the document correspond to :param sfe: source file.
- Can create the document if needed."""
- assert sfe != 0
- doc = self._fe_map.get(sfe, None)
- if doc is None:
- # Could be a document from outside...
- filename = pyutils.name_image(files_map.Get_File_Name(sfe))
- if not os.path.isabs(filename):
- dirname = pyutils.name_image(files_map.Get_Directory_Name(sfe))
- filename = os.path.join(dirname, filename)
- doc = self.create_document_from_sfe(sfe, filename)
- return doc
-
- def add_vhdl_file(self, name):
- log.info("loading %s", name)
- if os.path.isabs(name):
- absname = name
- else:
- absname = os.path.join(self._root_path, name)
- # Create a document for this file.
- try:
- fd = open(absname)
- sfe = document.Document.load(fd.read(), self._root_path, name)
- fd.close()
- except OSError as err:
- self._server.show_message(
- lsp.MessageType.Error, "cannot load {}: {}".format(name, err.strerror)
- )
- return
- doc = self.create_document_from_sfe(sfe, absname)
- doc.parse_document()
-
- def read_project(self):
- prj_file = os.path.join(self.root_path, "hdl-prj.json")
- if not os.path.exists(prj_file):
- log.info("project file %s does not exist", prj_file)
- return
- try:
- f = open(prj_file)
- except OSError as err:
- self._server.show_message(
- lsp.MessageType.Error,
- "cannot open project file {}: {}".format(prj_file, err.strerror),
- )
- return
- log.info("reading project file %s", prj_file)
- try:
- self._prj = json.load(f)
- except json.decoder.JSONDecodeError as e:
- log.info("error in project file")
- self._server.show_message(
- lsp.MessageType.Error,
- "json error in project file {}:{}:{}".format(
- prj_file, e.lineno, e.colno
- ),
- )
- f.close()
-
- def set_options_from_project(self):
- try:
- if self._prj is None:
- return
- if not isinstance(self._prj, dict):
- raise ProjectError("project file is not a dictionnary")
- opts = self._prj.get("options", None)
- if opts is None:
- return
- if not isinstance(opts, dict):
- raise ProjectError("'options' is not a dictionnary")
- ghdl_opts = opts.get("ghdl_analysis", None)
- if ghdl_opts is None:
- return
- log.info("Using options: %s", ghdl_opts)
- for opt in ghdl_opts:
- if not libghdl.set_option(opt.encode("utf-8")):
- self._server.show_message(
- lsp.MessageType.Error, "error with option: {}".format(opt)
- )
- except ProjectError as e:
- self._server.show_message(
- lsp.MessageType.Error, "error in project file: {}".format(e.msg)
- )
-
- def read_files_from_project(self):
- try:
- files = self._prj.get("files", [])
- if not isinstance(files, list):
- raise ProjectError("'files' is not a list")
- for f in files:
- if not isinstance(f, dict):
- raise ProjectError("an element of 'files' is not a dict")
- name = f.get("file")
- if not isinstance(name, str):
- raise ProjectError("a 'file' is not a string")
- lang = f.get("language", "vhdl")
- if lang == "vhdl":
- self.add_vhdl_file(name)
- except ProjectError as e:
- self._server.show_message(
- lsp.MessageType.Error, "error in project file: {}".format(e.msg)
- )
-
- def get_configuration(self):
- self._server.configuration(
- [{"scopeUri": "", "section": "vhdl.maxNumberOfProblems"}]
- )
-
- def gather_diagnostics(self, doc):
- # Gather messages (per file)
- nbr_msgs = errorout_memory.Get_Nbr_Messages()
- diags = {}
- diag = {}
- for i in range(nbr_msgs):
- hdr = errorout_memory.Get_Error_Record(i + 1)
- msg = errorout_memory.Get_Error_Message(i + 1).decode("utf-8")
- if hdr.file == 0:
- # Possible for error limit reached.
- continue
- err_range = {
- "start": {"line": hdr.line - 1, "character": hdr.offset},
- "end": {"line": hdr.line - 1, "character": hdr.offset + hdr.length},
- }
- if hdr.group <= errorout_memory.Msg_Main:
- if hdr.id <= errorout.Msgid.Msgid_Note:
- severity = lsp.DiagnosticSeverity.Information
- elif hdr.id <= errorout.Msgid.Msgid_Warning:
- severity = lsp.DiagnosticSeverity.Warning
- else:
- severity = lsp.DiagnosticSeverity.Error
- diag = {
- "source": "ghdl",
- "range": err_range,
- "message": msg,
- "severity": severity,
- }
- if hdr.group == errorout_memory.Msg_Main:
- diag["relatedInformation"] = []
- fdiag = diags.get(hdr.file, None)
- if fdiag is None:
- diags[hdr.file] = [diag]
- else:
- fdiag.append(diag)
- else:
- assert diag
- if True:
- doc = self.sfe_to_document(hdr.file)
- diag["relatedInformation"].append(
- {
- "location": {"uri": doc.uri, "range": err_range},
- "message": msg,
- }
- )
- errorout_memory.Clear_Errors()
- # Publish diagnostics
- for sfe, diag in diags.items():
- doc = self.sfe_to_document(sfe)
- self.publish_diagnostics(doc.uri, diag)
- if doc is not None and doc._fe not in diags:
- # Clear previous diagnostics for the doc.
- self.publish_diagnostics(doc.uri, [])
-
- def obsolete_dependent_units(self, unit, antideps):
- """Obsolete units that depends of :param unit:"""
- udeps = antideps.get(unit, None)
- if udeps is None:
- # There are no units.
- return
- # Avoid infinite recursion
- antideps[unit] = None
- for un in udeps:
- log.debug(
- "obsolete %d %s", un, pyutils.name_image(nodes.Get_Identifier(un))
- )
- # Recurse
- self.obsolete_dependent_units(un, antideps)
- if nodes.Set_Date_State(un) == nodes.Date_State.Disk:
- # Already obsolete!
- continue
- # FIXME: just de-analyze ?
- nodes.Set_Date_State(un, nodes.Date_State.Disk)
- sem_lib.Free_Dependence_List(un)
- loc = nodes.Get_Location(un)
- fil = files_map.Location_To_File(loc)
- pos = files_map.Location_File_To_Pos(loc, fil)
- line = files_map.Location_File_To_Line(loc, fil)
- col = files_map.Location_File_Line_To_Offset(loc, fil, line)
- nodes.Set_Design_Unit_Source_Pos(un, pos)
- nodes.Set_Design_Unit_Source_Line(un, line)
- nodes.Set_Design_Unit_Source_Col(un, col)
-
- def obsolete_doc(self, doc):
- if doc._tree == nodes.Null_Iir:
- return
- # Free old tree
- assert nodes.Get_Kind(doc._tree) == nodes.Iir_Kind.Design_File
- if self._last_linted_doc == doc:
- antideps = None
- else:
- antideps = self.compute_anti_dependences()
- unit = nodes.Get_First_Design_Unit(doc._tree)
- while unit != nodes.Null_Iir:
- if antideps is not None:
- self.obsolete_dependent_units(unit, antideps)
- # FIXME: free unit; it is not referenced.
- unit = nodes.Get_Chain(unit)
- libraries.Purge_Design_File(doc._tree)
- doc._tree = nodes.Null_Iir
-
- def lint(self, doc_uri):
- doc = self.get_document(doc_uri)
- self.obsolete_doc(doc)
- doc.compute_diags()
- self.gather_diagnostics(doc)
-
- def apply_changes(self, doc_uri, contentChanges, new_version):
- doc = self.get_document(doc_uri)
- assert doc is not None, "try to modify a non-loaded document"
- self.obsolete_doc(doc)
- prev_sfe = doc._fe
- for change in contentChanges:
- doc.apply_change(change)
- if doc._fe != prev_sfe:
- del self._fe_map[prev_sfe]
- self._fe_map[doc._fe] = doc
- # Like lint
- doc.compute_diags()
- self.gather_diagnostics(doc)
-
- def check_document(self, doc_uri, source):
- self._docs[doc_uri].check_document(source)
-
- def rm_document(self, doc_uri):
- pass
-
- def apply_edit(self, edit):
- return self._server.request("workspace/applyEdit", {"edit": edit})
-
- def publish_diagnostics(self, doc_uri, diagnostics):
- self._server.notify(
- "textDocument/publishDiagnostics",
- params={"uri": doc_uri, "diagnostics": diagnostics},
- )
-
- def show_message(self, message, msg_type=lsp.MessageType.Info):
- self._server.notify(
- "window/showMessage", params={"type": msg_type, "message": message}
- )
-
- def declaration_to_location(self, decl):
- "Convert declaration :param decl: to an LSP Location"
- decl_loc = nodes.Get_Location(decl)
- if decl_loc == std_package.Std_Location.value:
- # There is no real file for the std.standard package.
- return None
- if decl_loc == libraries.Library_Location.value:
- # Libraries declaration are virtual.
- return None
- fe = files_map.Location_To_File(decl_loc)
- doc = self.sfe_to_document(fe)
- res = {"uri": doc.uri}
- nid = nodes.Get_Identifier(decl)
- res["range"] = {
- "start": symbols.location_to_position(fe, decl_loc),
- "end": symbols.location_to_position(
- fe, decl_loc + name_table.Get_Name_Length(nid)
- ),
- }
- return res
-
- def goto_definition(self, doc_uri, position):
- decl = self._docs[doc_uri].goto_definition(position)
- if decl is None:
- return None
- decl_loc = self.declaration_to_location(decl)
- if decl_loc is None:
- return None
- res = [decl_loc]
- 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 x_show_all_files(self):
- res = []
- for fe in range(1, files_map.Get_Last_Source_File_Entry() + 1):
- doc = self._fe_map.get(fe, None)
- res.append(
- {
- "fe": fe,
- "uri": doc.uri if doc is not None else None,
- "name": pyutils.name_image(files_map.Get_File_Name(fe)),
- "dir": pyutils.name_image(files_map.Get_Directory_Name(fe)),
- }
- )
- return res
-
- def x_get_all_entities(self):
- res = []
- lib = libraries.Get_Libraries_Chain()
- while lib != nodes.Null_Iir:
- files = nodes.Get_Design_File_Chain(lib)
- ents = []
- while files != nodes.Null_Iir:
- units = nodes.Get_First_Design_Unit(files)
- while units != nodes.Null_Iir:
- unitlib = nodes.Get_Library_Unit(units)
- if nodes.Get_Kind(unitlib) == nodes.Iir_Kind.Entity_Declaration:
- ents.append(unitlib)
- units = nodes.Get_Chain(units)
- files = nodes.Get_Chain(files)
- ents = [pyutils.name_image(nodes.Get_Identifier(e)) for e in ents]
- lib_name = pyutils.name_image(nodes.Get_Identifier(lib))
- res.extend([{"name": n, "library": lib_name} for n in ents])
- lib = nodes.Get_Chain(lib)
- return res
-
- def x_get_entity_interface(self, library, name):
- def create_interfaces(inters):
- res = []
- while inters != nodes.Null_Iir:
- res.append(
- {
- "name": name_table.Get_Name_Ptr(
- nodes.Get_Identifier(inters)
- ).decode("latin-1")
- }
- )
- inters = nodes.Get_Chain(inters)
- return res
-
- # Find library
- lib_id = name_table.Get_Identifier(library.encode("utf-8"))
- lib = libraries.Get_Library_No_Create(lib_id)
- if lib == name_table.Null_Identifier:
- return None
- # Find entity
- ent_id = name_table.Get_Identifier(name.encode("utf-8"))
- unit = libraries.Find_Primary_Unit(lib, ent_id)
- if unit == nodes.Null_Iir:
- return None
- ent = nodes.Get_Library_Unit(unit)
- return {
- "library": library,
- "entity": name,
- "generics": create_interfaces(nodes.Get_Generic_Chain(ent)),
- "ports": create_interfaces(nodes.Get_Port_Chain(ent)),
- }
-
- def compute_anti_dependences(self):
- """Return a dictionnary of anti dependencies for design unit"""
- res = {}
- lib = libraries.Get_Libraries_Chain()
- while lib != nodes.Null_Iir:
- files = nodes.Get_Design_File_Chain(lib)
- while files != nodes.Null_Iir:
- units = nodes.Get_First_Design_Unit(files)
- while units != nodes.Null_Iir:
- if nodes.Get_Date_State(units) == nodes.Date_State.Analyze:
- # The unit has been analyzed, so the dependencies are know.
- deps = nodes.Get_Dependence_List(units)
- assert deps != nodes.Null_Iir_List
- deps_it = lists.Iterate(deps)
- while lists.Is_Valid(byref(deps_it)):
- el = lists.Get_Element(byref(deps_it))
- if nodes.Get_Kind(el) == nodes.Iir_Kind.Design_Unit:
- if res.get(el, None):
- res[el].append(units)
- else:
- res[el] = [units]
- else:
- assert False
- lists.Next(byref(deps_it))
- units = nodes.Get_Chain(units)
- files = nodes.Get_Chain(files)
- lib = nodes.Get_Chain(lib)
- return res