Skip to content

Commit

Permalink
Merge pull request #18 from samtkit/semantic-tokens
Browse files Browse the repository at this point in the history
Implement "Semantic Tokens", "Go to declaration" and "Find references"
PascalHonegger authored May 7, 2023
2 parents 2a28e0e + 372364a commit a79e66f
Showing 25 changed files with 1,587 additions and 440 deletions.
2 changes: 1 addition & 1 deletion cli/src/main/kotlin/tools/samt/cli/ASTPrinter.kt
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@ internal object ASTPrinter {
}

private fun dumpInfo(node: Node): String? = when (node) {
is FileNode -> gray(node.sourceFile.path.path)
is FileNode -> gray(node.sourceFile.path.toString())
is RequestResponseOperationNode -> if (node.isAsync) red("async") else null
is IdentifierNode -> yellow(node.name)
is ImportBundleIdentifierNode -> yellow(node.name) + if (node.isWildcard) yellow(".*") else ""
2 changes: 1 addition & 1 deletion cli/src/main/kotlin/tools/samt/cli/DiagnosticFormatter.kt
Original file line number Diff line number Diff line change
@@ -141,7 +141,7 @@ internal class DiagnosticFormatter(

// -----> <file path>:<location>
append(gray(" ---> "))
append(diagnosticController.workingDirectory.relativize(errorSourceFilePath))
append(errorSourceFilePath.toString())
if (message.highlights.isNotEmpty()) {
val firstHighlight = message.highlights.first()
val firstHighlightLocation = firstHighlight.location
2 changes: 1 addition & 1 deletion cli/src/test/kotlin/tools/samt/cli/ASTPrinterTest.kt
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ class ASTPrinterTest {
val dumpWithoutColorCodes = dump.replace(Regex("\u001B\\[[;\\d]*m"), "")

assertEquals("""
FileNode /tmp/ASTPrinterTest.samt <1:1>
FileNode file:///tmp/ASTPrinterTest.samt <1:1>
├─WildcardImportNode <1:1>
│ └─ImportBundleIdentifierNode foo.bar.baz.* <1:8>
│ ├─IdentifierNode foo <1:8>
33 changes: 16 additions & 17 deletions cli/src/test/kotlin/tools/samt/cli/DiagnosticFormatterTest.kt
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ import tools.samt.parser.EnumDeclarationNode
import tools.samt.parser.FileNode
import tools.samt.parser.Parser
import java.net.URI
import kotlin.io.path.Path
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -40,8 +39,8 @@ class DiagnosticFormatterTest {

@Test
fun `file messages with no highlights`() {
val baseDirectory = Path("/tmp").toUri()
val filePath = Path("/tmp", "test.txt").toUri()
val baseDirectory = URI("file:///tmp")
val filePath = URI("file:///tmp/test.txt")
val controller = DiagnosticController(baseDirectory)
val source = ""
val sourceFile = SourceFile(filePath, source)
@@ -65,15 +64,15 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> test.txt
---> file:///tmp/test.txt
────────────────────────────────────────
WARNING: some warning
---> test.txt
---> file:///tmp/test.txt
────────────────────────────────────────
INFO: some info
---> test.txt
---> file:///tmp/test.txt
────────────────────────────────────────
FAILED in 0ms (1 error(s), 1 warning(s))
@@ -104,7 +103,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:2:1
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
1 │ package debug
|> 2 │ enum Test {
@@ -140,7 +139,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:2:1
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
1 │ package debug
|> 2 │ enum Test {
@@ -183,7 +182,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:3:5
---> file:///tmp/DiagnosticFormatterTest.samt:3:5
1 │ package debug
2 │ enum Test {
@@ -239,7 +238,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:3:5
---> file:///tmp/DiagnosticFormatterTest.samt:3:5
1 │ package debug
2 │ enum Test {
@@ -293,7 +292,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:3:5
---> file:///tmp/DiagnosticFormatterTest.samt:3:5
1 │ package debug
2 │ enum Test {
@@ -339,7 +338,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:3:5
---> file:///tmp/DiagnosticFormatterTest.samt:3:5
1 │ package debug
2 │ enum Test {
@@ -382,7 +381,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:2:1
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
1 │ package debug
2 │ enum Test {
@@ -420,7 +419,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:2:1
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
1 │ package debug
2 │ enum Test {
@@ -461,7 +460,7 @@ class DiagnosticFormatterTest {
assertEquals("""
────────────────────────────────────────
ERROR: some error
---> DiagnosticFormatterTest.samt:2:1
---> file:///tmp/DiagnosticFormatterTest.samt:2:1
1 │ package debug
2 │ enum Test {
@@ -480,8 +479,8 @@ class DiagnosticFormatterTest {
}

private fun parse(source: String): Triple<FileNode, DiagnosticContext, DiagnosticController> {
val baseDirectory = Path("/tmp").toUri()
val filePath = Path("/tmp", "DiagnosticFormatterTest.samt").toUri()
val baseDirectory = URI("file:///tmp")
val filePath = URI("file:///tmp/DiagnosticFormatterTest.samt")
val sourceFile = SourceFile(filePath, source)
val diagnosticController = DiagnosticController(baseDirectory)
val diagnosticContext = diagnosticController.getOrCreateContext(sourceFile)
2 changes: 1 addition & 1 deletion language-server/src/main/kotlin/tools/samt/ls/Mapping.kt
Original file line number Diff line number Diff line change
@@ -28,6 +28,6 @@ fun DiagnosticMessage.toDiagnostic(): Diagnostic? {
fun SamtLocation.toRange(): Range {
return Range(
Position(start.row, start.col),
Position(start.row, end.col)
Position(end.row, end.col)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package tools.samt.ls

import tools.samt.common.Location
import tools.samt.parser.BundleIdentifierNode
import tools.samt.parser.ExpressionNode
import tools.samt.parser.FileNode
import tools.samt.parser.IdentifierNode
import tools.samt.semantic.*

class SamtDeclarationLookup private constructor() : SamtSemanticLookup<Location, UserDeclared>() {
override fun markType(node: ExpressionNode, type: Type) {
super.markType(node, type)

if (type is UserDeclared) {
if (node is BundleIdentifierNode) {
this[node.components.last().location] = type
} else {
this[node.location] = type
}
}
}

override fun markOperationReference(operation: ServiceType.Operation, reference: IdentifierNode) {
super.markOperationReference(operation, reference)
this[reference.location] = operation
}

override fun markProviderDeclaration(providerType: ProviderType) {
super.markProviderDeclaration(providerType)
this[providerType.declaration.name.location] = providerType
}

override fun markServiceDeclaration(serviceType: ServiceType) {
super.markServiceDeclaration(serviceType)
this[serviceType.declaration.name.location] = serviceType
}

override fun markRecordDeclaration(recordType: RecordType) {
super.markRecordDeclaration(recordType)
this[recordType.declaration.name.location] = recordType
}

override fun markOperationDeclaration(operation: ServiceType.Operation) {
super.markOperationDeclaration(operation)
this[operation.declaration.name.location] = operation
}

companion object {
fun analyze(fileNode: FileNode, samtPackage: Package) =
SamtDeclarationLookup().also { it.analyze(fileNode, samtPackage) }
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package tools.samt.ls

import org.eclipse.lsp4j.*
import org.eclipse.lsp4j.jsonrpc.messages.Either
import org.eclipse.lsp4j.services.*
import tools.samt.common.*
import tools.samt.common.DiagnosticController
import tools.samt.common.collectSamtFiles
import tools.samt.common.readSamtSource
import java.io.Closeable
import java.net.URI
import java.util.concurrent.CompletableFuture
@@ -19,7 +22,14 @@ class SamtLanguageServer : LanguageServer, LanguageClientAware, Closeable {
CompletableFuture.supplyAsync {
buildSamtModel(params)
val capabilities = ServerCapabilities().apply {
setTextDocumentSync(TextDocumentSyncKind.Full)
textDocumentSync = Either.forLeft(TextDocumentSyncKind.Full)
semanticTokensProvider = SemanticTokensWithRegistrationOptions().apply {
legend = SamtSemanticTokens.legend
range = Either.forLeft(false)
full = Either.forLeft(true)
}
definitionProvider = Either.forLeft(true)
referencesProvider = Either.forLeft(true)
}
InitializeResult(capabilities)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package tools.samt.ls

import tools.samt.common.Location
import tools.samt.parser.BundleIdentifierNode
import tools.samt.parser.ExpressionNode
import tools.samt.parser.FileNode
import tools.samt.parser.IdentifierNode
import tools.samt.semantic.Package
import tools.samt.semantic.ServiceType
import tools.samt.semantic.Type
import tools.samt.semantic.UserDeclared

class SamtReferencesLookup private constructor() : SamtSemanticLookup<UserDeclared, List<Location>>() {
private fun addUsage(declaration: UserDeclared, usage: Location) {
if (this[declaration] == null) {
this[declaration] = mutableListOf()
}
(this[declaration] as MutableList<Location>) += usage
}

override fun markType(node: ExpressionNode, type: Type) {
super.markType(node, type)

if (type is UserDeclared) {
if (node is BundleIdentifierNode) {
addUsage(type, node.components.last().location)
} else {
addUsage(type, node.location)
}
}
}

override fun markOperationReference(operation: ServiceType.Operation, reference: IdentifierNode) {
super.markOperationReference(operation, reference)
addUsage(operation, reference.location)
}

companion object {
fun analyze(filesAndPackages: List<Pair<FileNode, Package>>): SamtReferencesLookup {
val lookup = SamtReferencesLookup()
for ((fileInfo, samtPackage) in filesAndPackages) {
lookup.analyze(fileInfo, samtPackage)
}
return lookup
}
}
}
Loading

0 comments on commit a79e66f

Please sign in to comment.