Skip to content

Commit

Permalink
Implement textDocument/hover request.
Browse files Browse the repository at this point in the history
Rebased from PR #1922

This will implement the method, but currently it is not offered
yet in the initialization to the editor; to be enabled once tests
finished. The implementation requires a global symbol table, which
is potentially a bit heavy. We might need to introduce the concept of
a per-file symbol table for faster local lookups.
  • Loading branch information
glatosinski authored and hzeller committed Feb 23, 2024
1 parent 0f97047 commit 65a6aec
Show file tree
Hide file tree
Showing 9 changed files with 359 additions and 23 deletions.
2 changes: 1 addition & 1 deletion verilog/analysis/symbol_table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ std::ostream& operator<<(std::ostream& stream, SymbolMetaType symbol_type) {
return SymbolMetaTypeNames().Unparse(symbol_type, stream);
}

static absl::string_view SymbolMetaTypeAsString(SymbolMetaType type) {
absl::string_view SymbolMetaTypeAsString(SymbolMetaType type) {
return SymbolMetaTypeNames().EnumName(type);
}

Expand Down
2 changes: 2 additions & 0 deletions verilog/analysis/symbol_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ enum class SymbolMetaType {

std::ostream& operator<<(std::ostream&, SymbolMetaType);

absl::string_view SymbolMetaTypeAsString(SymbolMetaType type);

// This classifies the type of reference that a single identifier is.
enum class ReferenceType {
// The base identifier in any chain.
Expand Down
19 changes: 19 additions & 0 deletions verilog/tools/ls/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,24 @@ cc_library(
],
)

cc_library(
name = "hover",
srcs = ["hover.cc"],
hdrs = ["hover.h"],
deps = [
":lsp-parse-buffer",
":symbol-table-handler",
"//common/lsp:lsp-protocol",
"//common/text:tree-context-visitor",
"//common/util:casts",
"//common/util:range",
"//verilog/CST:seq-block",
"//verilog/CST:verilog-nonterminals",
"//verilog/analysis:symbol-table",
"//verilog/parser:verilog-token-enum",
],
)

cc_library(
name = "symbol-table-handler",
srcs = ["symbol-table-handler.cc"],
Expand Down Expand Up @@ -185,6 +203,7 @@ cc_library(
srcs = ["verilog-language-server.cc"],
hdrs = ["verilog-language-server.h"],
deps = [
":hover",
":lsp-parse-buffer",
":symbol-table-handler",
":verible-lsp-adapter",
Expand Down
177 changes: 177 additions & 0 deletions verilog/tools/ls/hover.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright 2023 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "verilog/tools/ls/hover.h"

#include "common/text/tree_context_visitor.h"
#include "common/util/casts.h"
#include "common/util/range.h"
#include "verilog/CST/seq_block.h"
#include "verilog/CST/verilog_nonterminals.h"
#include "verilog/analysis/symbol_table.h"
#include "verilog/parser/verilog_token_enum.h"

namespace verilog {

namespace {

using verible::Symbol;
using verible::SyntaxTreeLeaf;
using verible::SyntaxTreeNode;
using verible::TreeContextVisitor;

// Finds names/labels of named blocks
class FindBeginLabel : public TreeContextVisitor {
public:
// Performs search of the label for end entry, based on its location in
// string and tags
absl::string_view LabelSearch(const verible::ConcreteSyntaxTree &tree,
absl::string_view substring, NodeEnum endtag,
NodeEnum begintag) {
substring_ = substring;
begintag_ = begintag;
endtag_ = endtag;
substring_found_ = false;
finished_ = false;
tree->Accept(this);
return label_;
}

private:
void Visit(const SyntaxTreeLeaf &leaf) final {
if (verible::IsSubRange(leaf.get().text(), substring_)) {
substring_found_ = true;
}
}

void Visit(const SyntaxTreeNode &node) final {
if (finished_) return;
const std::unique_ptr<Symbol> *lastbegin = nullptr;
for (const std::unique_ptr<Symbol> &child : node.children()) {
if (!child) continue;
if (child->Kind() == verible::SymbolKind::kLeaf &&
node.Tag().tag == static_cast<int>(endtag_)) {
Visit(verible::down_cast<const SyntaxTreeLeaf &>(*child));
if (substring_found_) return;
} else if (child->Tag().tag == static_cast<int>(begintag_)) {
lastbegin = &child;
} else if (child->Kind() == verible::SymbolKind::kNode) {
Visit(verible::down_cast<const SyntaxTreeNode &>(*child));
if (!label_.empty()) return;
if (substring_found_) {
if (!lastbegin) {
finished_ = true;
return;
}
const verible::TokenInfo *info = GetBeginLabelTokenInfo(**lastbegin);
finished_ = true;
if (!info) return;
label_ = info->text();
return;
}
}
if (finished_) return;
}
}

absl::string_view substring_;
NodeEnum endtag_;
NodeEnum begintag_;
absl::string_view label_;
bool substring_found_;
bool finished_;
};

// Constructs a Hover message for the given location
class HoverBuilder {
public:
HoverBuilder(SymbolTableHandler *symbol_table_handler,
const BufferTrackerContainer &tracker_container,
const verible::lsp::HoverParams &params)
: symbol_table_handler_(symbol_table_handler),
tracker_container_(tracker_container),
params_(params) {}

verible::lsp::Hover Build() {
std::optional<verible::TokenInfo> token =
symbol_table_handler_->GetTokenAtTextDocumentPosition(
params_, tracker_container_);
if (!token) return {};
verible::lsp::Hover response;
switch (token->token_enum()) {
case verilog_tokentype::TK_end:
HoverInfoEndToken(&response, *token);
break;
default:
HoverInfoIdentifier(&response, *token);
}
return response;
}

private:
void HoverInfoEndToken(verible::lsp::Hover *response,
const verible::TokenInfo &token) {
const verilog::BufferTracker *tracker =
tracker_container_.FindBufferTrackerOrNull(params_.textDocument.uri);
if (!tracker) return;
std::shared_ptr<const ParsedBuffer> parsedbuffer = tracker->current();
if (!parsedbuffer) return;
const verible::ConcreteSyntaxTree &tree =
parsedbuffer->parser().SyntaxTree();
if (!tree) return;
FindBeginLabel search;
absl::string_view label = search.LabelSearch(
tree, token.text(), NodeEnum::kEnd, NodeEnum::kBegin);
if (label.empty()) return;
response->contents.value = "### End of block\n\n";
absl::StrAppend(&response->contents.value, "---\n\nName: ", label,
"\n\n---");
}

void HoverInfoIdentifier(verible::lsp::Hover *response,
const verible::TokenInfo &token) {
absl::string_view symbol = token.text();
const SymbolTableNode *node =
symbol_table_handler_->FindDefinitionNode(symbol);
if (!node) return;
const SymbolInfo &info = node->Value();
response->contents.value = absl::StrCat(
"### ", SymbolMetaTypeAsString(info.metatype), " ", symbol, "\n\n");
if (!info.declared_type.syntax_origin && info.declared_type.implicit) {
absl::StrAppend(&response->contents.value,
"---\n\nType: (implicit)\n\n---");
} else if (info.declared_type.syntax_origin) {
absl::StrAppend(
&response->contents.value, "---\n\n", "Type: ",
verible::StringSpanOfSymbol(*info.declared_type.syntax_origin),
"\n\n---");
}
}

SymbolTableHandler *symbol_table_handler_;
const BufferTrackerContainer &tracker_container_;
const verible::lsp::HoverParams &params_;
};

} // namespace

verible::lsp::Hover CreateHoverInformation(
SymbolTableHandler *symbol_table_handler,
const BufferTrackerContainer &tracker, const verible::lsp::HoverParams &p) {
HoverBuilder builder(symbol_table_handler, tracker, p);
return builder.Build();
}

} // namespace verilog
30 changes: 30 additions & 0 deletions verilog/tools/ls/hover.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2023 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#ifndef VERILOG_TOOLS_LS_HOVER_H_INCLUDED
#define VERILOG_TOOLS_LS_HOVER_H_INCLUDED

#include "common/lsp/lsp-protocol.h"
#include "verilog/tools/ls/lsp-parse-buffer.h"
#include "verilog/tools/ls/symbol-table-handler.h"

namespace verilog {
// Provides hover information for given location
verible::lsp::Hover CreateHoverInformation(
SymbolTableHandler *symbol_table_handler,
const BufferTrackerContainer &tracker, const verible::lsp::HoverParams &p);
} // namespace verilog

#endif // hover_h_INCLUDED
34 changes: 22 additions & 12 deletions verilog/tools/ls/symbol-table-handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,10 @@ SymbolTableHandler::GetTokenInfoAtTextDocumentPosition(
return cursor_token;
}

absl::string_view SymbolTableHandler::GetTokenAtTextDocumentPosition(
std::optional<verible::TokenInfo>
SymbolTableHandler::GetTokenAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) {
const verilog::BufferTrackerContainer &parsed_buffers) const {
const verilog::BufferTracker *tracker =
parsed_buffers.FindBufferTrackerOrNull(params.textDocument.uri);
if (!tracker) {
Expand All @@ -310,8 +311,7 @@ absl::string_view SymbolTableHandler::GetTokenAtTextDocumentPosition(
params.position.character};
const verible::TextStructureView &text = parsedbuffer->parser().Data();

const verible::TokenInfo cursor_token = text.FindTokenAt(cursor);
return cursor_token.text();
return text.FindTokenAt(cursor);
}

verible::LineColumnRange
Expand Down Expand Up @@ -357,14 +357,17 @@ SymbolTableHandler::GetLocationFromSymbolName(
}

std::vector<verible::lsp::Location> SymbolTableHandler::FindDefinitionLocation(
const verible::lsp::DefinitionParams &params,
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) {
// TODO add iterating over multiple definitions
Prepare();
const std::string filepath = LSPUriToPath(params.textDocument.uri);
std::string relativepath = curr_project_->GetRelativePathToSource(filepath);
absl::string_view symbol =
std::optional<verible::TokenInfo> token =
GetTokenAtTextDocumentPosition(params, parsed_buffers);
if (!token) return {};
absl::string_view symbol = token->text();

VLOG(1) << "Looking for symbol: " << symbol;
VerilogSourceFile *reffile =
curr_project_->LookupRegisteredFile(relativepath);
Expand All @@ -390,11 +393,15 @@ std::vector<verible::lsp::Location> SymbolTableHandler::FindDefinitionLocation(
return locations;
}

const verible::Symbol *SymbolTableHandler::FindDefinitionSymbol(
const SymbolTableNode *SymbolTableHandler::FindDefinitionNode(
absl::string_view symbol) {
Prepare();
const SymbolTableNode *symbol_table_node =
ScanSymbolTreeForDefinition(&symbol_table_->Root(), symbol);
return ScanSymbolTreeForDefinition(&symbol_table_->Root(), symbol);
}

const verible::Symbol *SymbolTableHandler::FindDefinitionSymbol(
absl::string_view symbol) {
const SymbolTableNode *symbol_table_node = FindDefinitionNode(symbol);
if (symbol_table_node) return symbol_table_node->Value().syntax_origin;
return nullptr;
}
Expand All @@ -403,8 +410,10 @@ std::vector<verible::lsp::Location> SymbolTableHandler::FindReferencesLocations(
const verible::lsp::ReferenceParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) {
Prepare();
const absl::string_view symbol =
std::optional<verible::TokenInfo> token =
GetTokenAtTextDocumentPosition(params, parsed_buffers);
if (!token) return {};
const absl::string_view symbol = token->text();
const SymbolTableNode &root = symbol_table_->Root();
const SymbolTableNode *node = ScanSymbolTreeForDefinition(&root, symbol);
if (!node) {
Expand Down Expand Up @@ -440,9 +449,10 @@ SymbolTableHandler::FindRenameLocationsAndCreateEdits(
const verible::lsp::RenameParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) {
Prepare();

absl::string_view symbol =
std::optional<verible::TokenInfo> token =
GetTokenAtTextDocumentPosition(params, parsed_buffers);
if (!token) return {};
absl::string_view symbol = token->text();
const SymbolTableNode &root = symbol_table_->Root();
const SymbolTableNode *node = ScanSymbolTreeForDefinition(&root, symbol);
if (!node) return {};
Expand Down
18 changes: 10 additions & 8 deletions verilog/tools/ls/symbol-table-handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ class SymbolTableHandler {
// message delivered i.e. in textDocument/definition message.
// Provides a list of locations with symbol's definitions.
std::vector<verible::lsp::Location> FindDefinitionLocation(
const verible::lsp::DefinitionParams &params,
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers);

// Finds the node of the symbol table with definition for a given symbol.
const SymbolTableNode *FindDefinitionNode(absl::string_view symbol);

// Finds the symbol of the definition for the given identifier.
const verible::Symbol *FindDefinitionSymbol(absl::string_view symbol);

Expand All @@ -76,6 +79,12 @@ class SymbolTableHandler {
const verible::lsp::RenameParams &params,
const verilog::BufferTrackerContainer &parsed_buffers);

// Returns TokenInfo for token pointed by the LSP request based on
// TextDocumentPositionParams. If text is not found, nullopt is returned.
std::optional<verible::TokenInfo> GetTokenAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) const;

// Creates a symbol table for entire project (public: needed in unit-test)
std::vector<absl::Status> BuildProjectSymbolTable();

Expand All @@ -96,13 +105,6 @@ class SymbolTableHandler {
// method.
void ResetSymbolTable();

// Returns text pointed by the LSP request based on
// TextDocumentPositionParams. If text is not found, empty-initialized
// string_view is returned.
absl::string_view GetTokenAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers);

// Returns a range in which a token exists in the file by the LSP request
// based on TextDocumentPositionParams. If text is not found,
// empty-initialized LineColumnRange is returned.
Expand Down
Loading

0 comments on commit 65a6aec

Please sign in to comment.