Skip to content

Commit 45a255c

Browse files
authored
Merge pull request #32 from samtkit/ls-symbols
Language Server: Document Symbols
2 parents 9b387ee + 8f1fa33 commit 45a255c

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class SamtLanguageServer : LanguageServer, LanguageClientAware, Closeable {
5959
definitionProvider = Either.forLeft(true)
6060
referencesProvider = Either.forLeft(true)
6161
hoverProvider = Either.forLeft(true)
62+
documentSymbolProvider = Either.forLeft(true)
6263
}
6364
InitializeResult(capabilities)
6465
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ class SamtTextDocumentService(private val workspace: SamtWorkspace) : TextDocume
191191
}
192192
}
193193

194+
override fun documentSymbol(params: DocumentSymbolParams): CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> = CompletableFuture.supplyAsync {
195+
val path = params.textDocument.uri.toPathUri()
196+
val fileInfo = workspace.getFile(path) ?: return@supplyAsync emptyList()
197+
198+
fileInfo.fileNode?.getSymbols()?.map { Either.forRight<SymbolInformation, DocumentSymbol>(it) }.orEmpty()
199+
}
200+
194201
private fun UserDeclared.peekDeclaration(): String {
195202
fun List<ServiceType.Operation.Parameter>.toParameterList(): String =
196203
joinToString(", ") { it.peekDeclaration() }
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package tools.samt.ls
2+
3+
import org.eclipse.lsp4j.DocumentSymbol
4+
import org.eclipse.lsp4j.SymbolKind
5+
import tools.samt.parser.*
6+
7+
fun FileNode.getSymbols(): List<DocumentSymbol> = buildList {
8+
add(packageDeclaration.toSymbol())
9+
for (statement in statements) {
10+
when (statement) {
11+
is NamedDeclarationNode -> add(statement.toSymbol())
12+
is ConsumerDeclarationNode -> add(statement.toSymbol())
13+
is TypeImportNode, is WildcardImportNode -> {}
14+
is PackageDeclarationNode -> error("Unexpected package declaration")
15+
}
16+
}
17+
}
18+
19+
private fun NamedDeclarationNode.toSymbol(): DocumentSymbol {
20+
val kind = when (this) {
21+
is EnumDeclarationNode -> SymbolKind.Enum
22+
is ProviderDeclarationNode -> SymbolKind.Class
23+
is RecordDeclarationNode -> SymbolKind.Struct
24+
is ServiceDeclarationNode -> SymbolKind.Interface
25+
is TypeAliasNode -> SymbolKind.Class
26+
}
27+
val children = when (this) {
28+
is EnumDeclarationNode -> values.map { DocumentSymbol(it.name, SymbolKind.EnumMember, it.location.toRange(), it.location.toRange()) }
29+
is RecordDeclarationNode -> fields.map { DocumentSymbol(it.name.name, SymbolKind.Property, it.location.toRange(), it.name.location.toRange()) }
30+
is ServiceDeclarationNode -> operations.map { DocumentSymbol(it.name.name, SymbolKind.Method, it.location.toRange(), it.name.location.toRange()) }
31+
is ProviderDeclarationNode, is TypeAliasNode -> emptyList()
32+
}
33+
return DocumentSymbol(name.name, kind, location.toRange(), name.location.toRange()).apply {
34+
this.children = children
35+
}
36+
}
37+
38+
private fun ConsumerDeclarationNode.toSymbol() = DocumentSymbol("Consumer for ${providerName.name}", SymbolKind.Class, location.toRange(), providerName.location.toRange())
39+
40+
private fun PackageDeclarationNode.toSymbol() = DocumentSymbol(name.name, SymbolKind.Package, location.toRange(), name.location.toRange())
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package tools.samt.ls
2+
3+
import org.eclipse.lsp4j.DocumentSymbol
4+
import org.eclipse.lsp4j.Position
5+
import org.eclipse.lsp4j.Range
6+
import org.eclipse.lsp4j.SymbolKind
7+
import tools.samt.common.SourceFile
8+
import kotlin.test.Test
9+
import kotlin.test.assertEquals
10+
11+
class SymbolsTest {
12+
@Test
13+
fun `getSymbols returns correct symbols`() {
14+
val source = """
15+
package foo.bar
16+
enum Foo {
17+
A
18+
}
19+
record Bar {
20+
a: Int
21+
}
22+
service Baz {
23+
a()
24+
}
25+
provide BazProvider {
26+
implements Baz
27+
28+
transport http
29+
}
30+
consume BazProvider {
31+
uses Baz
32+
}
33+
""".trimIndent()
34+
val sourceFile = SourceFile("file:///tmp/test/src/model.samt".toPathUri(), source)
35+
val fileInfo = parseFile(sourceFile)
36+
val symbols = fileInfo.fileNode?.getSymbols()
37+
assertEquals(
38+
listOf(
39+
DocumentSymbol(
40+
"foo.bar",
41+
SymbolKind.Package,
42+
Range(Position(0, 0), Position(0, 15)),
43+
Range(Position(0, 8), Position(0, 15))
44+
),
45+
DocumentSymbol(
46+
"Foo",
47+
SymbolKind.Enum,
48+
Range(Position(1, 0), Position(3, 1)),
49+
Range(Position(1, 5), Position(1, 8))
50+
).apply {
51+
children = listOf(
52+
DocumentSymbol(
53+
"A",
54+
SymbolKind.EnumMember,
55+
Range(Position(2, 4), Position(2, 5)),
56+
Range(Position(2, 4), Position(2, 5))
57+
)
58+
)
59+
},
60+
DocumentSymbol(
61+
"Bar",
62+
SymbolKind.Struct,
63+
Range(Position(4, 0), Position(6, 1)),
64+
Range(Position(4, 7), Position(4, 10))
65+
).apply {
66+
children = listOf(
67+
DocumentSymbol(
68+
"a",
69+
SymbolKind.Property,
70+
Range(Position(5, 4), Position(5, 10)),
71+
Range(Position(5, 4), Position(5, 5))
72+
)
73+
)
74+
},
75+
DocumentSymbol(
76+
"Baz",
77+
SymbolKind.Interface,
78+
Range(Position(7, 0), Position(9, 1)),
79+
Range(Position(7, 8), Position(7, 11))
80+
).apply {
81+
children = listOf(
82+
DocumentSymbol(
83+
"a",
84+
SymbolKind.Method,
85+
Range(Position(8, 4), Position(8, 7)),
86+
Range(Position(8, 4), Position(8, 5))
87+
)
88+
)
89+
},
90+
DocumentSymbol(
91+
"BazProvider",
92+
SymbolKind.Class,
93+
Range(Position(10, 0), Position(14, 1)),
94+
Range(Position(10, 8), Position(10, 19))
95+
).apply { children = emptyList() },
96+
DocumentSymbol(
97+
"Consumer for BazProvider",
98+
SymbolKind.Class,
99+
Range(Position(15, 0), Position(17, 1)),
100+
Range(Position(15, 8), Position(15, 19))
101+
)
102+
),
103+
symbols
104+
)
105+
}
106+
}

0 commit comments

Comments
 (0)