Skip to content

Commit

Permalink
Merge pull request #2081 from hzeller/20240128-symbol-rename
Browse files Browse the repository at this point in the history
Add Symbol Renaming to the language server
  • Loading branch information
hzeller authored Jan 29, 2024
2 parents 2b14e0c + 6090dd4 commit 82ac518
Show file tree
Hide file tree
Showing 6 changed files with 734 additions and 5 deletions.
14 changes: 14 additions & 0 deletions common/lsp/lsp-protocol.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,17 @@ DocumentLinkParams:
DocumentLink:
range: Range
target?: string # DocumentUri

# -- textDocument/prepareRename
PrepareRenameParams:
<: TextDocumentPositionParams

# Response: Range

# -- textDocument/rename
RenameParams:
<: TextDocumentPositionParams
newName: string

# Response: Range[]

121 changes: 121 additions & 0 deletions verilog/tools/ls/symbol-table-handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "common/lsp/lsp-file-utils.h"
#include "common/lsp/lsp-protocol.h"
#include "common/strings/line_column_map.h"
#include "common/util/file_util.h"
#include "common/util/iterator_adaptors.h"
Expand Down Expand Up @@ -253,6 +254,29 @@ void SymbolTableHandler::Prepare() {
}
}

std::optional<verible::TokenInfo>
SymbolTableHandler::GetTokenInfoAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) {
const verilog::BufferTracker *tracker =
parsed_buffers.FindBufferTrackerOrNull(params.textDocument.uri);
if (!tracker) {
VLOG(1) << "Could not find buffer with URI " << params.textDocument.uri;
return {};
}
std::shared_ptr<const ParsedBuffer> parsedbuffer = tracker->current();
if (!parsedbuffer) {
VLOG(1) << "Buffer not found among opened buffers: "
<< params.textDocument.uri;
return {};
}
const verible::LineColumn cursor{params.position.line,
params.position.character};
const verible::TextStructureView &text = parsedbuffer->parser().Data();
const verible::TokenInfo cursor_token = text.FindTokenAt(cursor);
return cursor_token;
}

absl::string_view SymbolTableHandler::GetTokenAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) {
Expand All @@ -276,6 +300,30 @@ absl::string_view SymbolTableHandler::GetTokenAtTextDocumentPosition(
return cursor_token.text();
}

verible::LineColumnRange
SymbolTableHandler::GetTokenRangeAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &document_cursor,
const verilog::BufferTrackerContainer &parsed_buffers) {
const verilog::BufferTracker *tracker =
parsed_buffers.FindBufferTrackerOrNull(document_cursor.textDocument.uri);
if (!tracker) {
VLOG(1) << "Could not find buffer with URI "
<< document_cursor.textDocument.uri;
return {};
}
std::shared_ptr<const ParsedBuffer> parsedbuffer = tracker->current();
if (!parsedbuffer) {
VLOG(1) << "Buffer not found among opened buffers: "
<< document_cursor.textDocument.uri;
return {};
}
const verible::LineColumn cursor{document_cursor.position.line,
document_cursor.position.character};
const verible::TextStructureView &text = parsedbuffer->parser().Data();

const verible::TokenInfo cursor_token = text.FindTokenAt(cursor);
return text.GetRangeForToken(cursor_token);
}
std::optional<verible::lsp::Location>
SymbolTableHandler::GetLocationFromSymbolName(
absl::string_view symbol_name, const VerilogSourceFile *file_origin) {
Expand Down Expand Up @@ -355,6 +403,79 @@ std::vector<verible::lsp::Location> SymbolTableHandler::FindReferencesLocations(
return locations;
}

std::optional<verible::lsp::Range>
SymbolTableHandler::FindRenameableRangeAtCursor(
const verible::lsp::PrepareRenameParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) {
Prepare();
if (files_dirty_) {
BuildProjectSymbolTable();
}
std::optional<verible::TokenInfo> symbol =
GetTokenInfoAtTextDocumentPosition(params, parsed_buffers);
if (symbol) {
verible::TokenInfo token = symbol.value();
const SymbolTableNode &root = symbol_table_->Root();
const SymbolTableNode *node =
ScanSymbolTreeForDefinition(&root, token.text());
if (!node) return {};
return RangeFromLineColumn(
GetTokenRangeAtTextDocumentPosition(params, parsed_buffers));
}
return {};
}

verible::lsp::WorkspaceEdit
SymbolTableHandler::FindRenameLocationsAndCreateEdits(
const verible::lsp::RenameParams &params,
const verilog::BufferTrackerContainer &parsed_buffers) {
if (files_dirty_) {
BuildProjectSymbolTable();
}
Prepare();
absl::string_view symbol =
GetTokenAtTextDocumentPosition(params, parsed_buffers);
const SymbolTableNode &root = symbol_table_->Root();
const SymbolTableNode *node = ScanSymbolTreeForDefinition(&root, symbol);
if (!node) return {};
std::optional<verible::lsp::Location> location =
GetLocationFromSymbolName(*node->Key(), node->Value().file_origin);
if (!location) return {};
std::vector<verible::lsp::Location> locations;
locations.push_back(location.value());
std::vector<verible::lsp::TextEdit> textedits;
CollectReferences(&root, node, &locations);
if (locations.empty()) return {};
std::map<absl::string_view, std::vector<verible::lsp::TextEdit>>
file_edit_pairs;
for (const auto &loc : locations) {
file_edit_pairs[loc.uri].reserve(locations.size());
}
for (const auto &loc : locations) {
// TODO(jbylicki): Remove this band-aid fix once #1678 is merged - it should
// fix
// duplicate definition/references appending in modules and remove the need
// for adding the definition location above.
if (std::none_of(
file_edit_pairs[loc.uri].begin(), file_edit_pairs[loc.uri].end(),
[&loc](const verible::lsp::TextEdit &it) {
return loc.range.start.character == it.range.start.character &&
loc.range.start.line == it.range.end.line;
})) {
file_edit_pairs[loc.uri].push_back(verible::lsp::TextEdit({
.range = loc.range,
.newText = params.newName,
}));
}
}
files_dirty_ = true;
verible::lsp::WorkspaceEdit edit = verible::lsp::WorkspaceEdit{
.changes = {},
};
edit.changes = file_edit_pairs;
std::cerr << "SIZE: " << edit.changes[locations[0].uri].size() << std::endl;
return edit;
}
void SymbolTableHandler::CollectReferencesReferenceComponents(
const ReferenceComponentNode *ref, const SymbolTableNode *ref_origin,
const SymbolTableNode *definition_node,
Expand Down
30 changes: 25 additions & 5 deletions verilog/tools/ls/symbol-table-handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,18 @@ class SymbolTableHandler {
const verible::lsp::ReferenceParams &params,
const verilog::BufferTrackerContainer &parsed_buffers);

std::optional<verible::lsp::Range> FindRenameableRangeAtCursor(
const verible::lsp::PrepareRenameParams &params,
const verilog::BufferTrackerContainer &parsed_buffers);
// Provide new parsed content for the given path. If "content" is nullptr,
// opens the given file instead.
void UpdateFileContent(absl::string_view path,
const verilog::VerilogAnalyzer *parsed);

verible::lsp::WorkspaceEdit FindRenameLocationsAndCreateEdits(
const verible::lsp::RenameParams &params,
const verilog::BufferTrackerContainer &parsed_buffers);

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

Expand All @@ -91,6 +98,20 @@ class SymbolTableHandler {
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.
verible::LineColumnRange GetTokenRangeAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &document_cursor,
const verilog::BufferTrackerContainer &parsed_buffers);

// Returns a TokenInfo of a token pointed at by the cursor in the file by the
// LSP request based on TextDocumentPositionParams. If text is not found,
// nullptr is returned.
std::optional<verible::TokenInfo> GetTokenInfoAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers);

// Returns the Location of the symbol name in source file
// pointed by the file_origin.
// If given symbol name is not found, std::nullopt is returned.
Expand All @@ -115,9 +136,8 @@ class SymbolTableHandler {
const SymbolTableNode *definition_node,
std::vector<verible::lsp::Location> *references);

// Looks for verible.filelist file down in directory structure and loads data
// to project.
// It is meant to be executed once per VerilogProject setup
// Looks for verible.filelist file down in directory structure and loads
// data to project. It is meant to be executed once per VerilogProject setup
bool LoadProjectFileList(absl::string_view current_dir);

// Parse all the files in the project.
Expand All @@ -126,8 +146,8 @@ class SymbolTableHandler {
// Path to the filelist file for the project
std::string filelist_path_;

// Last timestamp of filelist file - used to check whether SymbolTable should
// be updated
// Last timestamp of filelist file - used to check whether SymbolTable
// should be updated
absl::optional<std::filesystem::file_time_type> last_filelist_update_;

// tells that symbol table should be rebuilt due to changes in files
Expand Down
Loading

0 comments on commit 82ac518

Please sign in to comment.