Skip to content

Commit 8af95de

Browse files
committed
Add rename support
1 parent 4c1d3ac commit 8af95de

File tree

5 files changed

+83
-8
lines changed

5 files changed

+83
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1313
- Can toggle whether non-script instances are included in the generated sourcemap (included by default).
1414
- Added support for "Find References"
1515
- Currently only works for finding all references of a local variable in the current document. Cross-file references will come in future.
16+
- Added support for "Rename"
17+
- Currently only works for local variables in the current document. Cross-file references will come in future.
1618

1719
## [1.0.0] - 2022-05-19
1820

src/Workspace.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,54 @@ lsp::ReferenceResult WorkspaceFolder::references(const lsp::ReferenceParams& par
857857
return result;
858858
}
859859

860+
lsp::RenameResult WorkspaceFolder::rename(const lsp::RenameParams& params)
861+
{
862+
// Verify the new name is valid (is an identifier)
863+
if (params.newName.length() == 0)
864+
throw JsonRpcException(lsp::ErrorCode::RequestFailed, "The new name must be a valid identifier");
865+
if (!isalpha(params.newName.at(0)) && params.newName.at(0) != '_')
866+
throw JsonRpcException(lsp::ErrorCode::RequestFailed, "The new name must be a valid identifier starting with a character or underscore");
867+
for (auto ch : params.newName)
868+
{
869+
if (!isalpha(ch) && !isdigit(ch) && ch != '_')
870+
throw JsonRpcException(
871+
lsp::ErrorCode::RequestFailed, "The new name must be a valid identifier composed of characters, digits, and underscores only");
872+
}
873+
874+
// TODO: currently we only support renaming local bindings in the current file
875+
auto moduleName = getModuleName(params.textDocument.uri);
876+
auto position = convertPosition(params.position);
877+
878+
// Run the type checker to ensure we are up to date
879+
// TODO: we only need the parse result here - can typechecking be skipped?
880+
if (frontend.isDirty(moduleName))
881+
frontend.check(moduleName);
882+
883+
auto sourceModule = frontend.getSourceModule(moduleName);
884+
auto module = frontend.moduleResolver.getModule(moduleName);
885+
if (!sourceModule || !module)
886+
throw JsonRpcException(lsp::ErrorCode::RequestFailed, "Unable to read source code");
887+
888+
auto exprOrLocal = Luau::findExprOrLocalAtPosition(*sourceModule, position);
889+
Luau::Symbol symbol;
890+
891+
if (exprOrLocal.getLocal())
892+
symbol = exprOrLocal.getLocal();
893+
else if (auto exprLocal = exprOrLocal.getExpr()->as<Luau::AstExprLocal>())
894+
symbol = exprLocal->local;
895+
else
896+
throw JsonRpcException(lsp::ErrorCode::RequestFailed, "Rename is currently only supported for local variable bindings in the current file");
897+
898+
auto references = findSymbolReferences(*sourceModule, symbol);
899+
std::vector<lsp::TextEdit> localChanges;
900+
for (auto& location : references)
901+
{
902+
localChanges.emplace_back(lsp::TextEdit{{convertPosition(location.begin), convertPosition(location.end)}, params.newName});
903+
}
904+
905+
return lsp::WorkspaceEdit{{{params.textDocument.uri.toString(), localChanges}}};
906+
}
907+
860908
// std::optional<std::vector<lsp::DocumentSymbol>> WorkspaceFolder::documentSymbol(const lsp::DocumentSymbolParams& params)
861909
// {
862910
// auto moduleName = getModuleName(params.textDocument.uri);

src/include/LSP/Protocol.hpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,13 @@ struct ServerCapabilities
288288
bool referencesProvider = false;
289289
bool documentSymbolProvider = false;
290290
std::optional<DocumentLinkOptions> documentLinkProvider;
291+
bool renameProvider = false;
291292
std::optional<DiagnosticOptions> diagnosticProvider;
292293
std::optional<WorkspaceCapabilities> workspace;
293294
};
294295
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ServerCapabilities, textDocumentSync, completionProvider, hoverProvider, signatureHelpProvider,
295296
declarationProvider, definitionProvider, typeDefinitionProvider, implementationProvider, referencesProvider, documentSymbolProvider,
296-
documentLinkProvider, diagnosticProvider, workspace);
297+
documentLinkProvider, renameProvider, diagnosticProvider, workspace);
297298

298299
struct InitializeResult
299300
{
@@ -832,6 +833,21 @@ struct DocumentSymbol
832833
};
833834
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(DocumentSymbol, name, detail, kind, tags, deprecated, range, selectionRange, children);
834835

836+
struct WorkspaceEdit
837+
{
838+
// TODO: this is optional and there are other options provided
839+
std::unordered_map<std::string /* DocumentUri */, std::vector<TextEdit>> changes;
840+
};
841+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(WorkspaceEdit, changes);
842+
843+
struct RenameParams : TextDocumentPositionParams
844+
{
845+
std::string newName;
846+
};
847+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(RenameParams, textDocument, position, newName);
848+
849+
using RenameResult = std::optional<WorkspaceEdit>;
850+
835851
struct WorkspaceFoldersChangeEvent
836852
{
837853
std::vector<WorkspaceFolder> added;
@@ -845,13 +861,6 @@ struct DidChangeWorkspaceFoldersParams
845861
};
846862
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(DidChangeWorkspaceFoldersParams, event);
847863

848-
struct WorkspaceEdit
849-
{
850-
// TODO: this is optional and there are other options provided
851-
std::unordered_map<std::string /* DocumentUri */, std::vector<TextEdit>> changes;
852-
};
853-
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(WorkspaceEdit, changes);
854-
855864
struct ApplyWorkspaceEditParams
856865
{
857866
std::optional<std::string> label;

src/include/LSP/Workspace.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class WorkspaceFolder
6868
std::optional<lsp::Location> gotoTypeDefinition(const lsp::TypeDefinitionParams& params);
6969

7070
lsp::ReferenceResult references(const lsp::ReferenceParams& params);
71+
lsp::RenameResult rename(const lsp::RenameParams& params);
7172

7273
// std::optional<std::vector<lsp::DocumentSymbol>> documentSymbol(const lsp::DocumentSymbolParams& params);
7374

src/main.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ class LanguageServer
103103
capabilities.documentSymbolProvider = false;
104104
// Document Link Provider
105105
capabilities.documentLinkProvider = {false};
106+
// Rename Provider
107+
capabilities.renameProvider = true;
106108
// Diagnostics Provider
107109
capabilities.diagnosticProvider = {"luau", /* interFileDependencies: */ true, /* workspaceDiagnostics: */ false};
108110
// Workspaces
@@ -157,6 +159,10 @@ class LanguageServer
157159
{
158160
return references(REQUIRED_PARAMS(params, "textDocument/references"));
159161
}
162+
else if (method == "textDocument/rename")
163+
{
164+
return rename(REQUIRED_PARAMS(params, "textDocument/rename"));
165+
}
160166
// else if (method == "textDocument/documentSymbol")
161167
// {
162168
// return documentSymbol(REQUIRED_PARAMS(params, "textDocument/documentSymbol"));
@@ -555,6 +561,15 @@ class LanguageServer
555561
// return nullptr;
556562
// }
557563

564+
Response rename(const lsp::RenameParams& params)
565+
{
566+
auto workspace = findWorkspace(params.textDocument.uri);
567+
auto result = workspace->rename(params);
568+
if (result)
569+
return *result;
570+
return nullptr;
571+
}
572+
558573
lsp::DocumentDiagnosticReport documentDiagnostic(const lsp::DocumentDiagnosticParams& params)
559574
{
560575
auto workspace = findWorkspace(params.textDocument.uri);

0 commit comments

Comments
 (0)