Skip to content

Commit eaaa2e0

Browse files
committed
Merge pull request #319 (rename)
2 parents b7f1b66 + 42ac545 commit eaaa2e0

File tree

8 files changed

+121
-2
lines changed

8 files changed

+121
-2
lines changed

server/src/main/kotlin/org/javacs/kt/KotlinLanguageServer.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class KotlinLanguageServer : LanguageServer, LanguageClientAware, Closeable {
7676
serverCapabilities.workspace.workspaceFolders.supported = true
7777
serverCapabilities.workspace.workspaceFolders.changeNotifications = Either.forRight(true)
7878
serverCapabilities.hoverProvider = Either.forLeft(true)
79+
serverCapabilities.renameProvider = Either.forLeft(true)
7980
serverCapabilities.completionProvider = CompletionOptions(false, listOf("."))
8081
serverCapabilities.signatureHelpProvider = SignatureHelpOptions(listOf("(", ","))
8182
serverCapabilities.definitionProvider = Either.forLeft(true)

server/src/main/kotlin/org/javacs/kt/KotlinTextDocumentService.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.javacs.kt.util.parseURI
2626
import org.javacs.kt.util.describeURI
2727
import org.javacs.kt.util.describeURIs
2828
import org.javacs.kt.commands.JAVA_TO_KOTLIN_COMMAND
29+
import org.javacs.kt.rename.renameSymbol
2930
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
3031
import java.net.URI
3132
import java.io.Closeable
@@ -144,8 +145,9 @@ class KotlinTextDocumentService(
144145
TODO("not implemented")
145146
}
146147

147-
override fun rename(params: RenameParams): CompletableFuture<WorkspaceEdit> {
148-
TODO("not implemented")
148+
override fun rename(params: RenameParams) = async.compute {
149+
val (file, cursor) = recover(params, Recompile.NEVER)
150+
renameSymbol(file, cursor, sp, params.newName)
149151
}
150152

151153
override fun completion(position: CompletionParams) = async.compute {

server/src/main/kotlin/org/javacs/kt/references/FindReferences.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.eclipse.lsp4j.Location
44
import org.javacs.kt.LOG
55
import org.javacs.kt.SourcePath
66
import org.javacs.kt.position.location
7+
import org.javacs.kt.position.toURIString
78
import org.javacs.kt.util.emptyResult
89
import org.javacs.kt.util.findParent
910
import org.javacs.kt.util.preOrderTraversal
@@ -31,9 +32,22 @@ fun findReferences(file: Path, cursor: Int, sp: SourcePath): List<Location> {
3132
.sortedWith(compareBy({ it.getUri() }, { it.getRange().getStart().getLine() }))
3233
}
3334

35+
fun findReferences(declaration: KtNamedDeclaration, sp: SourcePath): List<Location> {
36+
return doFindReferences(declaration, sp)
37+
.map { location(it) }
38+
.filterNotNull()
39+
.toList()
40+
.sortedWith(compareBy({ it.getUri() }, { it.getRange().getStart().getLine() }))
41+
}
42+
3443
private fun doFindReferences(file: Path, cursor: Int, sp: SourcePath): Collection<KtElement> {
3544
val recover = sp.currentVersion(file.toUri())
3645
val element = recover.elementAtPoint(cursor)?.findParent<KtNamedDeclaration>() ?: return emptyResult("No declaration at ${recover.describePosition(cursor)}")
46+
return doFindReferences(element, sp)
47+
}
48+
49+
private fun doFindReferences(element: KtNamedDeclaration, sp: SourcePath): Collection<KtElement> {
50+
val recover = sp.currentVersion(element.containingFile.toPath().toUri())
3751
val declaration = recover.compile[BindingContext.DECLARATION_TO_DESCRIPTOR, element] ?: return emptyResult("Declaration ${element.fqName} has no descriptor")
3852
val maybes = possibleReferences(declaration, sp).map { it.toPath() }
3953
LOG.debug("Scanning {} files for references to {}", maybes.size, element.fqName)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.javacs.kt.rename
2+
3+
import org.eclipse.lsp4j.*
4+
import org.eclipse.lsp4j.jsonrpc.messages.Either
5+
import org.javacs.kt.CompiledFile
6+
import org.javacs.kt.SourcePath
7+
import org.javacs.kt.position.location
8+
import org.javacs.kt.references.findReferences
9+
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
10+
import org.jetbrains.kotlin.psi.KtNamedDeclaration
11+
12+
fun renameSymbol(file: CompiledFile, cursor: Int, sp: SourcePath, newName: String): WorkspaceEdit? {
13+
val (declaration, location) = findDeclaration(file, cursor) ?: return null
14+
return declaration.let {
15+
val declarationEdit = Either.forLeft<TextDocumentEdit, ResourceOperation>(TextDocumentEdit(
16+
VersionedTextDocumentIdentifier().apply { uri = location.uri },
17+
listOf(TextEdit(location.range, newName))
18+
))
19+
20+
val referenceEdits = findReferences(declaration, sp).map {
21+
Either.forLeft<TextDocumentEdit, ResourceOperation>(TextDocumentEdit(
22+
VersionedTextDocumentIdentifier().apply { uri = it.uri },
23+
listOf(TextEdit(it.range, newName))
24+
))
25+
}
26+
27+
WorkspaceEdit(listOf(declarationEdit) + referenceEdits)
28+
}
29+
}
30+
31+
private fun findDeclaration(file: CompiledFile, cursor: Int): Pair<KtNamedDeclaration, Location>? {
32+
val (_, target) = file.referenceAtPoint(cursor) ?: return null
33+
val psi = target.findPsi()
34+
35+
return if (psi is KtNamedDeclaration) {
36+
psi.nameIdentifier?.let {
37+
location(it)?.let { location ->
38+
Pair(psi, location)
39+
}
40+
}
41+
} else {
42+
null
43+
}
44+
}

server/src/test/kotlin/org/javacs/kt/LanguageServerTestFixture.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ abstract class LanguageServerTestFixture(relativeWorkspaceRoot: String) : Langua
5454
println("Memory after test: ${total - free} MB used / $total MB total")
5555
}
5656

57+
fun renameParams(relativePath: String, line: Int, column: Int, newName: String): RenameParams =
58+
textDocumentPosition(relativePath, line, column).run { RenameParams(textDocument, position, newName) }
59+
5760
fun completionParams(relativePath: String, line: Int, column: Int): CompletionParams {
5861
val file = workspaceRoot.resolve(relativePath)
5962
val fileId = TextDocumentIdentifier(file.toUri().toString())
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.javacs.kt
2+
3+
import org.eclipse.lsp4j.Position
4+
import org.eclipse.lsp4j.Range
5+
import org.hamcrest.Matchers.containsString
6+
import org.hamcrest.Matchers.equalTo
7+
import org.hamcrest.Matchers.hasItem
8+
import org.hamcrest.Matchers.hasProperty
9+
import org.hamcrest.Matchers.hasSize
10+
import org.junit.Assert.assertThat
11+
import org.junit.Test
12+
13+
class RenameReferenceTest : SingleFileTestFixture("rename", "SomeClass.kt") {
14+
15+
@Test
16+
fun `rename in the reference`() {
17+
val edits = languageServer.textDocumentService.rename(renameParams(file, 4, 26, "NewClassName")).get()!!
18+
val changes = edits.documentChanges
19+
20+
assertThat(changes.size, equalTo(3))
21+
assertThat(changes[0].left.textDocument.uri, containsString("SomeOtherClass.kt"))
22+
23+
assertThat(changes[0].left.edits[0].newText, equalTo("NewClassName"))
24+
assertThat(changes[0].left.edits[0].range.start, equalTo(Position(2, 6)))
25+
assertThat(changes[0].left.edits[0].range.end, equalTo(Position(2, 20)))
26+
}
27+
}
28+
29+
class RenameDefinitionTest : SingleFileTestFixture("rename", "SomeOtherClass.kt") {
30+
31+
@Test
32+
fun `rename in the definition`() {
33+
val edits = languageServer.textDocumentService.rename(renameParams(file, 2, 15, "NewClassName")).get()!!
34+
val changes = edits.documentChanges
35+
36+
println(changes)
37+
38+
assertThat(changes.size, equalTo(3))
39+
assertThat(changes[0].left.textDocument.uri, containsString("SomeOtherClass.kt"))
40+
41+
assertThat(changes[0].left.edits[0].newText, equalTo("NewClassName"))
42+
assertThat(changes[0].left.edits[0].range.start, equalTo(Position(2, 6)))
43+
assertThat(changes[0].left.edits[0].range.end, equalTo(Position(2, 20)))
44+
}
45+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package rename
2+
3+
class SomeClass {
4+
5+
val something: SomeOtherClass = SomeOtherClass()
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package rename
2+
3+
class SomeOtherClass {
4+
}

0 commit comments

Comments
 (0)