From a2ae26ea4bc23420c65cf86725b105d176ea16c5 Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Wed, 3 Nov 2021 08:17:01 +0100 Subject: [PATCH] Basic implementation of language server document highlight. If the cursor is on a symbol, this will highlight all the symbols in the buffer with the same name (it does _not_ take naming scopes into account yet). Signed-off-by: Henner Zeller --- common/lsp/lsp-protocol.yaml | 13 ++++++++++ verilog/tools/ls/BUILD | 1 + verilog/tools/ls/README.md | 3 ++- verilog/tools/ls/verible-lsp-adapter.cc | 32 +++++++++++++++++++++++++ verilog/tools/ls/verible-lsp-adapter.h | 8 +++++++ verilog/tools/ls/verilog_ls.cc | 18 +++++++++----- 6 files changed, 68 insertions(+), 7 deletions(-) diff --git a/common/lsp/lsp-protocol.yaml b/common/lsp/lsp-protocol.yaml index ca39900b6..296754bf5 100644 --- a/common/lsp/lsp-protocol.yaml +++ b/common/lsp/lsp-protocol.yaml @@ -49,6 +49,11 @@ TextDocumentContentChangeEvent: range?: Range # Range optional; if no range given, full document replace text: string +# For hover or highlight +TextDocumentPositionParams: + textDocument: TextDocumentIdentifier + position: Position + # -- Text document notifiations. These allow us to keep track of the content. DidOpenTextDocumentParams: # textDocument/didOpen textDocument: TextDocumentItem @@ -108,3 +113,11 @@ DocumentSymbol: # children: DocumentSymbol[]. Since we can't to that recursively in jcxxgen, # these are directly emitted as json children?: object = nullptr + +# -- textDocument/documentHighlight +DocumentHighlightParams: + <: TextDocumentPositionParams + +DocumentHighlight: # response documentHighlight is [] of this. + range: Range + # there is also a highlight kind to distinguish read/write diff --git a/verilog/tools/ls/BUILD b/verilog/tools/ls/BUILD index aad2983ef..09340a9df 100644 --- a/verilog/tools/ls/BUILD +++ b/verilog/tools/ls/BUILD @@ -36,6 +36,7 @@ cc_library( "//common/text:text_structure", "//verilog/analysis:verilog_analyzer", "//verilog/analysis:verilog_linter", + "//verilog/parser:verilog_token_enum", "@jsonhpp", ], ) diff --git a/verilog/tools/ls/README.md b/verilog/tools/ls/README.md index 7024c41c2..126a5727e 100644 --- a/verilog/tools/ls/README.md +++ b/verilog/tools/ls/README.md @@ -17,7 +17,8 @@ progress - [x] Provide code actions for autofixes provided by lint rules - [x] Generate file symbol outline ('navigation tree') - [ ] Provide formatting. - - [ ] Highlight all the symbols that are the same as current under cursor. + - [x] Highlight all the symbols that are the same as current under cursor. + - [ ] Take scope and type into account to only highlight _same_ symbols. - [ ] Find definition of symbol even if in another file. - [ ] Rename refactor a symbol diff --git a/verilog/tools/ls/verible-lsp-adapter.cc b/verilog/tools/ls/verible-lsp-adapter.cc index 613db8aae..bcf153bd9 100644 --- a/verilog/tools/ls/verible-lsp-adapter.cc +++ b/verilog/tools/ls/verible-lsp-adapter.cc @@ -21,6 +21,7 @@ #include "nlohmann/json.hpp" #include "verilog/analysis/verilog_analyzer.h" #include "verilog/analysis/verilog_linter.h" +#include "verilog/parser/verilog_token_enum.h" #include "verilog/tools/ls/document-symbol-filler.h" #include "verilog/tools/ls/lsp-parse-buffer.h" @@ -172,4 +173,35 @@ nlohmann::json CreateDocumentSymbolOutline( return toplevel.children; } +std::vector CreateHighlightRanges( + const BufferTracker *tracker, + const verible::lsp::DocumentHighlightParams &p) { + std::vector result; + if (!tracker) return result; + const ParsedBuffer *const current = tracker->current(); + if (!current) return result; + const verible::LineColumn cursor{p.position.line, p.position.character}; + const verible::TextStructureView &text = current->parser().Data(); + const verible::TokenInfo cursor_token = text.FindTokenAt(cursor); + + if (cursor_token.token_enum() == SymbolIdentifier) { + // Find all the symbols with the same name in the buffer. + // Note, this is very simplistic as it does _not_ take scopes into account. + // For that, we'd need the symbol table, but that implementation is not + // complete yet. + for (const verible::TokenInfo &tok : text.TokenStream()) { + if (tok.token_enum() != cursor_token.token_enum()) continue; + if (tok.text() != cursor_token.text()) continue; + const verible::LineColumnRange range = text.GetRangeForToken(tok); + result.push_back(verible::lsp::DocumentHighlight{ + .range = { + .start = {.line = range.start.line, + .character = range.start.column}, + .end = {.line = range.end.line, .character = range.end.column}, + }}); + } + } + return result; +} + } // namespace verilog diff --git a/verilog/tools/ls/verible-lsp-adapter.h b/verilog/tools/ls/verible-lsp-adapter.h index 27baf8ebb..be4fc6031 100644 --- a/verilog/tools/ls/verible-lsp-adapter.h +++ b/verilog/tools/ls/verible-lsp-adapter.h @@ -42,5 +42,13 @@ std::vector GenerateLinterCodeActions( nlohmann::json CreateDocumentSymbolOutline( const BufferTracker *tracker, const verible::lsp::DocumentSymbolParams &p, bool kate_compatible_tags = true); + +// Give a position in a document, return ranges in the buffer that should +// be hi-lit. +// Current implementation: if cursor is over a symbol, highlight all symbols +// with the same name (NB: Does _not_ take scoping into account yet). +std::vector CreateHighlightRanges( + const BufferTracker *tracker, + const verible::lsp::DocumentHighlightParams &p); } // namespace verilog #endif // VERILOG_TOOLS_LS_VERIBLE_LSP_ADAPTER_H diff --git a/verilog/tools/ls/verilog_ls.cc b/verilog/tools/ls/verilog_ls.cc index a257e4b7c..984f98016 100644 --- a/verilog/tools/ls/verilog_ls.cc +++ b/verilog/tools/ls/verilog_ls.cc @@ -61,8 +61,9 @@ static InitializeResult InitializeServer(const nlohmann::json ¶ms) { {"change", 2}, // Incremental updates }, }, - {"codeActionProvider", true}, // Autofixes for lint errors - {"documentSymbolProvider", true}, // Outline of file + {"codeActionProvider", true}, // Autofixes for lint errors + {"documentSymbolProvider", true}, // Symbol-outline of file + {"documentHighlightProvider", true}, // Highlight same symbol }; return result; @@ -133,22 +134,27 @@ int main(int argc, char *argv[]) { // Exchange of capabilities. dispatcher.AddRequestHandler("initialize", InitializeServer); - // Provide autofixes - dispatcher.AddRequestHandler( + dispatcher.AddRequestHandler( // Provide autofixes "textDocument/codeAction", [&parsed_buffers](const verible::lsp::CodeActionParams &p) { return verilog::GenerateLinterCodeActions( parsed_buffers.FindBufferTrackerOrNull(p.textDocument.uri), p); }); - // Provide outline - dispatcher.AddRequestHandler( + dispatcher.AddRequestHandler( // Provide document outline/index "textDocument/documentSymbol", [&parsed_buffers](const verible::lsp::DocumentSymbolParams &p) { return verilog::CreateDocumentSymbolOutline( parsed_buffers.FindBufferTrackerOrNull(p.textDocument.uri), p); }); + dispatcher.AddRequestHandler( // Highlight related symbols under cursor + "textDocument/documentHighlight", + [&parsed_buffers](const verible::lsp::DocumentHighlightParams &p) { + return verilog::CreateHighlightRanges( + parsed_buffers.FindBufferTrackerOrNull(p.textDocument.uri), p); + }); + // The client sends a request to shut down. Use that to exit our loop. bool shutdown_requested = false; dispatcher.AddRequestHandler("shutdown",