Skip to content

Commit a79e66f

Browse files
Merge pull request #18 from samtkit/semantic-tokens
Implement "Semantic Tokens", "Go to declaration" and "Find references"
2 parents 2a28e0e + 372364a commit a79e66f

25 files changed

Lines changed: 1587 additions & 440 deletions

cli/src/main/kotlin/tools/samt/cli/ASTPrinter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ internal object ASTPrinter {
5656
}
5757

5858
private fun dumpInfo(node: Node): String? = when (node) {
59-
is FileNode -> gray(node.sourceFile.path.path)
59+
is FileNode -> gray(node.sourceFile.path.toString())
6060
is RequestResponseOperationNode -> if (node.isAsync) red("async") else null
6161
is IdentifierNode -> yellow(node.name)
6262
is ImportBundleIdentifierNode -> yellow(node.name) + if (node.isWildcard) yellow(".*") else ""

cli/src/main/kotlin/tools/samt/cli/DiagnosticFormatter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ internal class DiagnosticFormatter(
141141

142142
// -----> <file path>:<location>
143143
append(gray(" ---> "))
144-
append(diagnosticController.workingDirectory.relativize(errorSourceFilePath))
144+
append(errorSourceFilePath.toString())
145145
if (message.highlights.isNotEmpty()) {
146146
val firstHighlight = message.highlights.first()
147147
val firstHighlightLocation = firstHighlight.location

cli/src/test/kotlin/tools/samt/cli/ASTPrinterTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class ASTPrinterTest {
3636
val dumpWithoutColorCodes = dump.replace(Regex("\u001B\\[[;\\d]*m"), "")
3737

3838
assertEquals("""
39-
FileNode /tmp/ASTPrinterTest.samt <1:1>
39+
FileNode file:///tmp/ASTPrinterTest.samt <1:1>
4040
├─WildcardImportNode <1:1>
4141
│ └─ImportBundleIdentifierNode foo.bar.baz.* <1:8>
4242
│ ├─IdentifierNode foo <1:8>

cli/src/test/kotlin/tools/samt/cli/DiagnosticFormatterTest.kt

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import tools.samt.parser.EnumDeclarationNode
88
import tools.samt.parser.FileNode
99
import tools.samt.parser.Parser
1010
import java.net.URI
11-
import kotlin.io.path.Path
1211
import kotlin.test.Test
1312
import kotlin.test.assertEquals
1413
import kotlin.test.assertFalse
@@ -40,8 +39,8 @@ class DiagnosticFormatterTest {
4039

4140
@Test
4241
fun `file messages with no highlights`() {
43-
val baseDirectory = Path("/tmp").toUri()
44-
val filePath = Path("/tmp", "test.txt").toUri()
42+
val baseDirectory = URI("file:///tmp")
43+
val filePath = URI("file:///tmp/test.txt")
4544
val controller = DiagnosticController(baseDirectory)
4645
val source = ""
4746
val sourceFile = SourceFile(filePath, source)
@@ -65,15 +64,15 @@ class DiagnosticFormatterTest {
6564
assertEquals("""
6665
────────────────────────────────────────
6766
ERROR: some error
68-
---> test.txt
67+
---> file:///tmp/test.txt
6968
7069
────────────────────────────────────────
7170
WARNING: some warning
72-
---> test.txt
71+
---> file:///tmp/test.txt
7372
7473
────────────────────────────────────────
7574
INFO: some info
76-
---> test.txt
75+
---> file:///tmp/test.txt
7776
7877
────────────────────────────────────────
7978
FAILED in 0ms (1 error(s), 1 warning(s))
@@ -104,7 +103,7 @@ class DiagnosticFormatterTest {
104103
assertEquals("""
105104
────────────────────────────────────────
106105
ERROR: some error
107-
---> DiagnosticFormatterTest.samt:2:1
106+
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
108107
109108
1 │ package debug
110109
|> 2 │ enum Test {
@@ -140,7 +139,7 @@ class DiagnosticFormatterTest {
140139
assertEquals("""
141140
────────────────────────────────────────
142141
ERROR: some error
143-
---> DiagnosticFormatterTest.samt:2:1
142+
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
144143
145144
1 │ package debug
146145
|> 2 │ enum Test {
@@ -183,7 +182,7 @@ class DiagnosticFormatterTest {
183182
assertEquals("""
184183
────────────────────────────────────────
185184
ERROR: some error
186-
---> DiagnosticFormatterTest.samt:3:5
185+
---> file:///tmp/DiagnosticFormatterTest.samt:3:5
187186
188187
1 │ package debug
189188
2 │ enum Test {
@@ -239,7 +238,7 @@ class DiagnosticFormatterTest {
239238
assertEquals("""
240239
────────────────────────────────────────
241240
ERROR: some error
242-
---> DiagnosticFormatterTest.samt:3:5
241+
---> file:///tmp/DiagnosticFormatterTest.samt:3:5
243242
244243
1 │ package debug
245244
2 │ enum Test {
@@ -293,7 +292,7 @@ class DiagnosticFormatterTest {
293292
assertEquals("""
294293
────────────────────────────────────────
295294
ERROR: some error
296-
---> DiagnosticFormatterTest.samt:3:5
295+
---> file:///tmp/DiagnosticFormatterTest.samt:3:5
297296
298297
1 │ package debug
299298
2 │ enum Test {
@@ -339,7 +338,7 @@ class DiagnosticFormatterTest {
339338
assertEquals("""
340339
────────────────────────────────────────
341340
ERROR: some error
342-
---> DiagnosticFormatterTest.samt:3:5
341+
---> file:///tmp/DiagnosticFormatterTest.samt:3:5
343342
344343
1 │ package debug
345344
2 │ enum Test {
@@ -382,7 +381,7 @@ class DiagnosticFormatterTest {
382381
assertEquals("""
383382
────────────────────────────────────────
384383
ERROR: some error
385-
---> DiagnosticFormatterTest.samt:2:1
384+
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
386385
387386
1 │ package debug
388387
2 │ enum Test {
@@ -420,7 +419,7 @@ class DiagnosticFormatterTest {
420419
assertEquals("""
421420
────────────────────────────────────────
422421
ERROR: some error
423-
---> DiagnosticFormatterTest.samt:2:1
422+
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
424423
425424
1 │ package debug
426425
2 │ enum Test {
@@ -461,7 +460,7 @@ class DiagnosticFormatterTest {
461460
assertEquals("""
462461
────────────────────────────────────────
463462
ERROR: some error
464-
---> DiagnosticFormatterTest.samt:2:1
463+
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
465464
466465
1 │ package debug
467466
2 │ enum Test {
@@ -480,8 +479,8 @@ class DiagnosticFormatterTest {
480479
}
481480

482481
private fun parse(source: String): Triple<FileNode, DiagnosticContext, DiagnosticController> {
483-
val baseDirectory = Path("/tmp").toUri()
484-
val filePath = Path("/tmp", "DiagnosticFormatterTest.samt").toUri()
482+
val baseDirectory = URI("file:///tmp")
483+
val filePath = URI("file:///tmp/DiagnosticFormatterTest.samt")
485484
val sourceFile = SourceFile(filePath, source)
486485
val diagnosticController = DiagnosticController(baseDirectory)
487486
val diagnosticContext = diagnosticController.getOrCreateContext(sourceFile)

language-server/src/main/kotlin/tools/samt/ls/Mapping.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ fun DiagnosticMessage.toDiagnostic(): Diagnostic? {
2828
fun SamtLocation.toRange(): Range {
2929
return Range(
3030
Position(start.row, start.col),
31-
Position(start.row, end.col)
31+
Position(end.row, end.col)
3232
)
3333
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package tools.samt.ls
2+
3+
import tools.samt.common.Location
4+
import tools.samt.parser.BundleIdentifierNode
5+
import tools.samt.parser.ExpressionNode
6+
import tools.samt.parser.FileNode
7+
import tools.samt.parser.IdentifierNode
8+
import tools.samt.semantic.*
9+
10+
class SamtDeclarationLookup private constructor() : SamtSemanticLookup<Location, UserDeclared>() {
11+
override fun markType(node: ExpressionNode, type: Type) {
12+
super.markType(node, type)
13+
14+
if (type is UserDeclared) {
15+
if (node is BundleIdentifierNode) {
16+
this[node.components.last().location] = type
17+
} else {
18+
this[node.location] = type
19+
}
20+
}
21+
}
22+
23+
override fun markOperationReference(operation: ServiceType.Operation, reference: IdentifierNode) {
24+
super.markOperationReference(operation, reference)
25+
this[reference.location] = operation
26+
}
27+
28+
override fun markProviderDeclaration(providerType: ProviderType) {
29+
super.markProviderDeclaration(providerType)
30+
this[providerType.declaration.name.location] = providerType
31+
}
32+
33+
override fun markServiceDeclaration(serviceType: ServiceType) {
34+
super.markServiceDeclaration(serviceType)
35+
this[serviceType.declaration.name.location] = serviceType
36+
}
37+
38+
override fun markRecordDeclaration(recordType: RecordType) {
39+
super.markRecordDeclaration(recordType)
40+
this[recordType.declaration.name.location] = recordType
41+
}
42+
43+
override fun markOperationDeclaration(operation: ServiceType.Operation) {
44+
super.markOperationDeclaration(operation)
45+
this[operation.declaration.name.location] = operation
46+
}
47+
48+
companion object {
49+
fun analyze(fileNode: FileNode, samtPackage: Package) =
50+
SamtDeclarationLookup().also { it.analyze(fileNode, samtPackage) }
51+
}
52+
}

language-server/src/main/kotlin/tools/samt/ls/SamtLanguageServer.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package tools.samt.ls
22

33
import org.eclipse.lsp4j.*
4+
import org.eclipse.lsp4j.jsonrpc.messages.Either
45
import org.eclipse.lsp4j.services.*
5-
import tools.samt.common.*
6+
import tools.samt.common.DiagnosticController
7+
import tools.samt.common.collectSamtFiles
8+
import tools.samt.common.readSamtSource
69
import java.io.Closeable
710
import java.net.URI
811
import java.util.concurrent.CompletableFuture
@@ -19,7 +22,14 @@ class SamtLanguageServer : LanguageServer, LanguageClientAware, Closeable {
1922
CompletableFuture.supplyAsync {
2023
buildSamtModel(params)
2124
val capabilities = ServerCapabilities().apply {
22-
setTextDocumentSync(TextDocumentSyncKind.Full)
25+
textDocumentSync = Either.forLeft(TextDocumentSyncKind.Full)
26+
semanticTokensProvider = SemanticTokensWithRegistrationOptions().apply {
27+
legend = SamtSemanticTokens.legend
28+
range = Either.forLeft(false)
29+
full = Either.forLeft(true)
30+
}
31+
definitionProvider = Either.forLeft(true)
32+
referencesProvider = Either.forLeft(true)
2333
}
2434
InitializeResult(capabilities)
2535
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package tools.samt.ls
2+
3+
import tools.samt.common.Location
4+
import tools.samt.parser.BundleIdentifierNode
5+
import tools.samt.parser.ExpressionNode
6+
import tools.samt.parser.FileNode
7+
import tools.samt.parser.IdentifierNode
8+
import tools.samt.semantic.Package
9+
import tools.samt.semantic.ServiceType
10+
import tools.samt.semantic.Type
11+
import tools.samt.semantic.UserDeclared
12+
13+
class SamtReferencesLookup private constructor() : SamtSemanticLookup<UserDeclared, List<Location>>() {
14+
private fun addUsage(declaration: UserDeclared, usage: Location) {
15+
if (this[declaration] == null) {
16+
this[declaration] = mutableListOf()
17+
}
18+
(this[declaration] as MutableList<Location>) += usage
19+
}
20+
21+
override fun markType(node: ExpressionNode, type: Type) {
22+
super.markType(node, type)
23+
24+
if (type is UserDeclared) {
25+
if (node is BundleIdentifierNode) {
26+
addUsage(type, node.components.last().location)
27+
} else {
28+
addUsage(type, node.location)
29+
}
30+
}
31+
}
32+
33+
override fun markOperationReference(operation: ServiceType.Operation, reference: IdentifierNode) {
34+
super.markOperationReference(operation, reference)
35+
addUsage(operation, reference.location)
36+
}
37+
38+
companion object {
39+
fun analyze(filesAndPackages: List<Pair<FileNode, Package>>): SamtReferencesLookup {
40+
val lookup = SamtReferencesLookup()
41+
for ((fileInfo, samtPackage) in filesAndPackages) {
42+
lookup.analyze(fileInfo, samtPackage)
43+
}
44+
return lookup
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)