| // Copyright 2021 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/document-symbol-filler.h" |
| |
| #include "common/lsp/lsp-protocol-enums.h" |
| #include "common/lsp/lsp-protocol.h" |
| #include "common/util/value_saver.h" |
| #include "verilog/CST/class.h" |
| #include "verilog/CST/functions.h" |
| #include "verilog/CST/module.h" |
| #include "verilog/CST/package.h" |
| #include "verilog/CST/seq_block.h" |
| |
| // Magic value to hint that we have to fill out the start range. |
| static constexpr int kUninitializedStartLine = -1; |
| |
| namespace verilog { |
| DocumentSymbolFiller::DocumentSymbolFiller( |
| bool kate_workaround, const verible::TextStructureView &text, |
| verible::lsp::DocumentSymbol *toplevel) |
| : kModuleSymbolKind(kate_workaround ? verible::lsp::SymbolKind::Method |
| : verible::lsp::SymbolKind::Module), |
| kBlockSymbolKind(kate_workaround ? verible::lsp::SymbolKind::Class |
| : verible::lsp::SymbolKind::Namespace), |
| text_view_(text), |
| current_symbol_(toplevel) { |
| toplevel->range.start = {.line = 0, .character = 0}; |
| } |
| |
| void DocumentSymbolFiller::Visit(const verible::SyntaxTreeLeaf &leaf) { |
| verible::lsp::Range range = RangeFromLeaf(leaf); |
| if (current_symbol_->range.start.line == kUninitializedStartLine) { |
| // We're the first concrete token with a position within our parent, |
| // set the start position. |
| current_symbol_->range.start = range.start; |
| } |
| // Update the end position with every token we see. The last one wins. |
| current_symbol_->range.end = range.end; |
| } |
| |
| void DocumentSymbolFiller::Visit(const verible::SyntaxTreeNode &node) { |
| verible::lsp::DocumentSymbol *parent = current_symbol_; |
| |
| // These things can probably be done easier with Matchers. |
| verible::lsp::DocumentSymbol node_symbol; |
| node_symbol.range.start.line = kUninitializedStartLine; |
| bool is_visible_node = false; |
| switch (static_cast<verilog::NodeEnum>(node.Tag().tag)) { |
| case verilog::NodeEnum::kModuleDeclaration: { |
| const auto *name_leaf = verilog::GetModuleName(node); |
| if (name_leaf) { |
| is_visible_node = true; |
| node_symbol.kind = kModuleSymbolKind; |
| node_symbol.selectionRange = RangeFromLeaf(*name_leaf); |
| node_symbol.name = std::string(name_leaf->get().text()); |
| } |
| break; |
| } |
| |
| case verilog::NodeEnum::kSeqBlock: |
| case verilog::NodeEnum::kGenerateBlock: |
| if (!node.children().empty()) { |
| const auto &begin = node.children().front().get(); |
| if (begin) { |
| if (const auto *token = GetBeginLabelTokenInfo(*begin); token) { |
| is_visible_node = true; |
| node_symbol.kind = kBlockSymbolKind; |
| node_symbol.selectionRange = RangeFromToken(*token); |
| node_symbol.name = std::string(token->text()); |
| } |
| } |
| } |
| break; |
| |
| case verilog::NodeEnum::kClassDeclaration: { |
| const auto *class_name_leaf = verilog::GetClassName(node); |
| if (class_name_leaf) { |
| is_visible_node = true; |
| node_symbol.kind = verible::lsp::SymbolKind::Class; |
| node_symbol.selectionRange = RangeFromToken(class_name_leaf->get()); |
| node_symbol.name = std::string(class_name_leaf->get().text()); |
| } |
| break; |
| } |
| |
| case verilog::NodeEnum::kPackageDeclaration: { |
| const auto *package_name = verilog::GetPackageNameToken(node); |
| if (package_name) { |
| is_visible_node = true; |
| node_symbol.kind = verible::lsp::SymbolKind::Package; |
| node_symbol.selectionRange = RangeFromToken(*package_name); |
| node_symbol.name = std::string(package_name->text()); |
| } |
| break; |
| } |
| |
| case verilog::NodeEnum::kFunctionDeclaration: { |
| const auto &function_name = verilog::GetFunctionName(node); |
| if (function_name) { |
| is_visible_node = true; |
| node_symbol.kind = verible::lsp::SymbolKind::Function; |
| node_symbol.selectionRange = RangeFromToken(function_name->get()); |
| node_symbol.name = std::string(function_name->get().text()); |
| } |
| break; |
| } |
| |
| default: |
| is_visible_node = false; |
| break; |
| } |
| |
| // Independent of visible or not, we always descend to our children. |
| if (is_visible_node) { |
| const verible::ValueSaver<verible::lsp::DocumentSymbol *> value_saver( |
| ¤t_symbol_, &node_symbol); |
| for (const auto &child : node.children()) { |
| if (child) child->Accept(this); |
| } |
| // Update our parent with what we found |
| if (parent->children == nullptr) { |
| if (parent->range.start.line == kUninitializedStartLine) { |
| parent->range.start = node_symbol.range.start; |
| } |
| parent->children = nlohmann::json::array(); |
| parent->has_children = true; |
| } |
| parent->children.push_back(node_symbol); |
| parent->range.end = node_symbol.range.end; |
| } else { |
| for (const auto &child : node.children()) { |
| if (child) child->Accept(this); |
| } |
| } |
| } |
| |
| verible::lsp::Range DocumentSymbolFiller::RangeFromLeaf( |
| const verible::SyntaxTreeLeaf &leaf) const { |
| return RangeFromToken(leaf.get()); |
| } |
| |
| verible::lsp::Range DocumentSymbolFiller::RangeFromToken( |
| const verible::TokenInfo &token) const { |
| const verible::LineColumnRange range = text_view_.GetRangeForToken(token); |
| return {.start = {.line = range.start.line, .character = range.start.column}, |
| .end = {.line = range.end.line, .character = range.end.column}}; |
| } |
| } // namespace verilog |