Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Symbol Renaming to the language server #2081

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -25,6 +25,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/range.h"
Expand Down Expand Up @@ -233,6 +234,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 @@ -256,6 +280,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 @@ -335,6 +383,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 @@ -67,11 +67,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 @@ -90,6 +97,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 @@ -114,9 +135,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 @@ -125,8 +145,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
Loading