@@ -124,7 +124,7 @@ proc getProjectFile(fileUri: string, ls: LanguageServer): Future[string] {.async
124124 rootPath = AbsoluteDir (ls.initializeParams.rootUri.uriToPath)
125125 pathRelativeToRoot = string (AbsoluteFile (fileUri).relativeTo (rootPath))
126126 mappings = ls.getWorkspaceConfiguration.await ().projectMapping.get (@ [])
127-
127+
128128 for mapping in mappings:
129129 if find (cstring (pathRelativeToRoot), re (mapping.fileRegex), 0 , pathRelativeToRoot.len) != - 1 :
130130 result = string (rootPath) / mapping.projectFile
@@ -162,30 +162,45 @@ proc initialize(ls: LanguageServer, params: InitializeParams):
162162 Future [InitializeResult ] {.async .} =
163163 debug " Initialize received..."
164164 ls.initializeParams = params
165- return InitializeResult (
165+ result = InitializeResult (
166166 capabilities: ServerCapabilities (
167167 textDocumentSync: some (% TextDocumentSyncOptions (
168168 openClose: some (true ),
169169 change: some (TextDocumentSyncKind .Full .int ),
170170 willSave: some (false ),
171171 willSaveWaitUntil: some (false ),
172- save: some (SaveOptions (includeText: some (true ))))),
172+ save: some (SaveOptions (includeText: some (true ))))
173+ ),
173174 hoverProvider: some (true ),
174175 workspace: WorkspaceCapability (
175- workspaceFolders: some (WorkspaceFolderCapability ())),
176+ workspaceFolders: some (WorkspaceFolderCapability ())
177+ ),
176178 completionProvider: CompletionOptions (
177179 triggerCharacters: some (@ [" ." ]),
178- resolveProvider: some (false )),
180+ resolveProvider: some (false )
181+ ),
179182 definitionProvider: some (true ),
180183 declarationProvider: some (true ),
181184 typeDefinitionProvider: some (true ),
182185 referencesProvider: some (true ),
183186 documentHighlightProvider: some (true ),
184187 workspaceSymbolProvider: some (true ),
185188 executeCommandProvider: ExecuteCommandOptions (
186- commands: some (@ [RESTART_COMMAND , RECOMPILE_COMMAND , CHECK_PROJECT_COMMAND ])),
189+ commands: some (@ [RESTART_COMMAND , RECOMPILE_COMMAND , CHECK_PROJECT_COMMAND ])
190+ ),
187191 documentSymbolProvider: some (true ),
188- codeActionProvider: some (true )))
192+ codeActionProvider: some (true )
193+ )
194+ )
195+ # Support rename by default, but check if we can also support prepare
196+ result .capabilities.renameProvider = % true
197+ if params.capabilities.textDocument.isSome:
198+ let docCaps = params.capabilities.textDocument.unsafeGet ()
199+ # Check if the client support prepareRename
200+ if docCaps.rename.isSome and docCaps.rename.get ().prepareSupport.get (false ):
201+ result .capabilities.renameProvider = %* {
202+ " prepareProvider" : true
203+ }
189204
190205proc initialized (ls: LanguageServer , _: JsonNode ):
191206 Future [void ] {.async .} =
@@ -692,6 +707,45 @@ proc references(ls: LanguageServer, params: ReferenceParams):
692707 .filter (suggest => suggest.section != ideDef or includeDeclaration)
693708 .map (toLocation);
694709
710+ proc prepareRename (ls: LanguageServer , params: PrepareRenameParams ,
711+ id: int ): Future [JsonNode ] {.async .} =
712+ with (params.position, params.textDocument):
713+ let
714+ nimsuggest = await ls.getNimsuggest (uri)
715+ def = await nimsuggest.def (
716+ uriToPath (uri),
717+ ls.uriToStash (uri),
718+ line + 1 ,
719+ ls.getCharacter (uri, line, character)
720+ )
721+ if def.len == 0 :
722+ return newJNull ()
723+ # Check if the symbol belongs to the project
724+ let projectDir = ls.initializeParams.rootUri.uriToPath
725+ if def[0 ].filePath.isRelativeTo (projectDir):
726+ return % def[0 ].toLocation ().range
727+
728+ return newJNull ()
729+
730+ proc rename (ls: LanguageServer , params: RenameParams , id: int ): Future [WorkspaceEdit ] {.async .} =
731+ # We reuse the references command as to not duplicate it
732+ let references = await ls.references (ReferenceParams (
733+ context: ReferenceContext (includeDeclaration: true ),
734+ textDocument: params.textDocument,
735+ position: params.position
736+ ))
737+ # Build up list of edits that the client needs to perform for each file
738+ let projectDir = ls.initializeParams.rootUri.uriToPath
739+ var edits = newJObject ()
740+ for reference in references:
741+ # Only rename symbols in the project.
742+ # If client supports prepareRename then an error will already have been thrown
743+ if reference.uri.uriToPath ().isRelativeTo (projectDir):
744+ if reference.uri notin edits:
745+ edits[reference.uri] = newJArray ()
746+ edits[reference.uri] &= % TextEdit (range : reference.range , newText: params.newName)
747+ result = WorkspaceEdit (changes: some edits)
748+
695749proc codeAction (ls: LanguageServer , params: CodeActionParams ):
696750 Future [seq [CodeAction ]] {.async .} =
697751 let projectUri = await getProjectFile (params.textDocument.uri.uriToPath, ls)
@@ -849,6 +903,8 @@ proc registerHandlers*(connection: StreamConnection, pipeInput: AsyncInputStream
849903 connection.register (" textDocument/hover" , partial (hover, ls))
850904 connection.register (" textDocument/references" , partial (references, ls))
851905 connection.register (" textDocument/codeAction" , partial (codeAction, ls))
906+ connection.register (" textDocument/prepareRename" , partial (prepareRename, ls))
907+ connection.register (" textDocument/rename" , partial (rename, ls))
852908 connection.register (" workspace/executeCommand" , partial (executeCommand, ls))
853909 connection.register (" workspace/symbol" , partial (workspaceSymbol, ls))
854910 connection.register (" textDocument/documentHighlight" , partial (documentHighlight, ls))
0 commit comments