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

[LSP] Added Symbol Renaming to the language server #1948

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 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[]

122 changes: 122 additions & 0 deletions verilog/tools/ls/symbol-table-handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
#include "verilog/tools/ls/symbol-table-handler.h"

#include <filesystem>
#include <map>

#include "absl/flags/flag.h"
#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 @@ -221,6 +223,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,
jbylicki marked this conversation as resolved.
Show resolved Hide resolved
const verilog::BufferTrackerContainer &parsed_buffers) {
Expand All @@ -244,6 +269,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 @@ -317,6 +366,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.has_value()) {
jbylicki marked this conversation as resolved.
Show resolved Hide resolved
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 (auto &loc : locations) {
jbylicki marked this conversation as resolved.
Show resolved Hide resolved
file_edit_pairs[loc.uri].reserve(locations.size());
}
for (auto &loc : locations) {
jbylicki marked this conversation as resolved.
Show resolved Hide resolved
// 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
25 changes: 20 additions & 5 deletions verilog/tools/ls/symbol-table-handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,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 verible::TextStructureView *content);

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 @@ -88,6 +95,15 @@ class SymbolTableHandler {
const verible::lsp::TextDocumentPositionParams &params,
const verilog::BufferTrackerContainer &parsed_buffers);

// TODO(jbylicki): Add docstring
jbylicki marked this conversation as resolved.
Show resolved Hide resolved
verible::LineColumnRange GetTokenRangeAtTextDocumentPosition(
const verible::lsp::TextDocumentPositionParams &document_cursor,
const verilog::BufferTrackerContainer &parsed_buffers);

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 @@ -112,9 +128,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 @@ -123,8 +138,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