Skip to content

Commit 7c76226

Browse files
authored
Semantic tokens WIP (#4)
* WIP * More tokens * Use published version of langoustine
1 parent 128a4da commit 7c76226

File tree

10 files changed

+134
-30
lines changed

10 files changed

+134
-30
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
bin/
2+
.bsp
3+
.metals
4+
.scala-build
5+
.vscode

LSP.scala

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import langoustine.lsp.runtime.uinteger
1515

1616
import cats.syntax.all.*
1717
import langoustine.lsp.app.LangoustineApp
18+
import langoustine.lsp.tools.SemanticTokensEncoder
19+
import langoustine.lsp.tools.SemanticToken
20+
import cats.effect.std.Semaphore
1821

1922
object LSP extends LangoustineApp:
2023
import QuickmaffsLSP.{server, State}
@@ -85,10 +88,9 @@ object QuickmaffsLSP:
8588
processFile(Path(path))
8689

8790
def set(u: DocumentUri)(st: State) =
88-
state.update(_.updated(u, st)) <*
89-
cats.effect.std
90-
.Console[IO]
91-
.errorln(s"Setting $st for $u")
91+
state.update(_.updated(u, st)) <* IO.consoleForIO.errorln(
92+
s"State update: $u is set to ${st.getClass}"
93+
)
9294

9395
def get(u: DocumentUri) =
9496
state.get.map(_.get(u))
@@ -150,6 +152,15 @@ object QuickmaffsLSP:
150152
case _ => None
151153
}
152154

155+
val encoder = SemanticTokensEncoder(
156+
tokenTypes = Vector(
157+
SemanticTokenTypes.variable,
158+
SemanticTokenTypes.number,
159+
SemanticTokenTypes.operator
160+
),
161+
modifiers = Vector.empty
162+
)
163+
153164
LSPBuilder
154165
.create[IO]
155166
.handleRequest(initialize) { (in, back) =>
@@ -167,6 +178,12 @@ object QuickmaffsLSP:
167178
definitionProvider = Opt(true),
168179
documentSymbolProvider = Opt(true),
169180
renameProvider = Opt(true),
181+
semanticTokensProvider = Opt(
182+
SemanticTokensOptions(
183+
legend = encoder.legend,
184+
full = Opt(true)
185+
)
186+
),
170187
textDocumentSync = Opt(
171188
TextDocumentSyncOptions(
172189
openClose = Opt(true),
@@ -187,6 +204,72 @@ object QuickmaffsLSP:
187204
.handleNotification(textDocument.didSave) { (in, back) =>
188205
recompile(in.textDocument.uri, back)
189206
}
207+
.handleRequest(textDocument.semanticTokens.full) { (in, back) =>
208+
get(in.textDocument.uri).flatMap {
209+
case Some(State.Ok(idx, values, program)) =>
210+
val tokens = Vector.newBuilder[SemanticToken]
211+
program.statements.map(_.value).foreach { st =>
212+
st match
213+
case Statement.Ass(name, e) =>
214+
inline def nameToken(tok: Expr.Name[WithSpan]) =
215+
tokenFromSpan(tok.value.span, SemanticTokenTypes.variable)
216+
217+
inline def tokenFromSpan(
218+
span: Span,
219+
tpe: SemanticTokenTypes
220+
) =
221+
SemanticToken.fromRange(
222+
span.toRange,
223+
tokenType = tpe
224+
)
225+
226+
tokens += nameToken(name)
227+
228+
def go(expr: Expr[WithSpan]): Unit =
229+
expr match
230+
case Expr.Add(l, r, operator) =>
231+
go(l)
232+
go(r)
233+
tokens += tokenFromSpan(
234+
operator.span,
235+
SemanticTokenTypes.operator
236+
)
237+
238+
case Expr.Mul(l, r, operator) =>
239+
go(l)
240+
go(r)
241+
tokens += tokenFromSpan(
242+
operator.span,
243+
SemanticTokenTypes.operator
244+
)
245+
246+
case n @ Expr.Name(_) =>
247+
tokens += nameToken(n)
248+
249+
case Expr.Lit(value) =>
250+
tokens += tokenFromSpan(
251+
value.span,
252+
SemanticTokenTypes.number
253+
)
254+
255+
go(e)
256+
}
257+
258+
IO.consoleForIO
259+
.errorln(
260+
s"Sending over the following tokens: ${tokens.result().map(_.toString)}"
261+
) *>
262+
IO.fromEither(encoder.encode(tokens.result())).map(Opt(_))
263+
264+
case other =>
265+
IO.consoleForIO
266+
.errorln(
267+
s"Got weird state for ${in.textDocument.uri}: $other"
268+
)
269+
.as(Opt.empty)
270+
271+
}
272+
}
190273
.handleRequest(textDocument.definition) { (in, back) =>
191274
variableUnderCursor(in.textDocument.uri, in.position).map {
192275
foundMaybe =>

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ clean:
44
rm -rf bin/*
55
scala-cli clean .
66

7+
setup:
8+
scala-cli setup-ide *.scala
9+
10+
711
bin/repl:
812
mkdir -p bin
913
scala-cli package . --main-class REPL --force --output bin/repl
1014

1115
bin/lsp:
1216
mkdir -p bin
13-
scala-cli package . --main-class LSP --force --output bin/lsp
17+
scala-cli package . --main-class LSP --force --output bin/lsp
1418

1519
bin/quickmaffs:
1620
mkdir -p bin

build.dependencies.scala

Lines changed: 0 additions & 4 deletions
This file was deleted.

compiler.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ object QuickmaffsCompiler:
1919
def compile(p: Program[WithSpan]): Result =
2020
val errors = Vector.newBuilder[CompileError]
2121

22-
def indexReferences(e: Expr[WithSpan]) =
22+
def indexReferences(e: Expr[WithSpan]): Map[String, Vector[Span]] =
2323
inline def pair(e1: Expr[WithSpan], e2: Expr[WithSpan]) =
2424
val m1 = go(e1)
2525
val m2 = go(e2)
@@ -33,8 +33,8 @@ object QuickmaffsCompiler:
3333
expr match
3434
case _: Lit[?] => Map.empty
3535
case Name(WithSpan(pos, name)) => Map(name -> Vector(pos))
36-
case Add(e1, e2) => pair(e1, e2)
37-
case Mul(e1, e2) => pair(e1, e2)
36+
case Add(e1, e2, _) => pair(e1, e2)
37+
case Mul(e1, e2, _) => pair(e1, e2)
3838

3939
go(e)
4040
end indexReferences

evaluator.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ object QuickmaffsEvaluator:
3737
case Expr.Name(v) =>
3838
state.get(v).toRight(EvaluationError.NameNotFound(v))
3939

40-
case Expr.Add(e1, e2) =>
40+
case Expr.Add(e1, e2, _) =>
4141
(evaluate(e1, state), evaluate(e2, state)).mapN(_ + _)
4242

43-
case Expr.Mul(e1, e2) =>
43+
case Expr.Mul(e1, e2, _) =>
4444
(evaluate(e1, state), evaluate(e2, state)).mapN(_ * _)
4545

4646
@targetName("evaluate_WithSpan")

examples/test.qmf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
worsts = (5 + 1)
22
x = (worsts + 11)
33
z = 17
4-
t = (x * 1)
4+
test = (x * 1)
55
h = (x * (11 + worsts))
66
hs = (x * (z + worsts))
7+
ts = 25
78
a = 11
89
r = (x + a)
910

examples/test2.qmf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
b = (5 + 1)
22
x = (b + 11)
33
s = 17
4-
t = (asd * 1)
54
h = (x * (11 + b))
65
hs = (x * (11 + s))
6+
7+
8+

parser.scala

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,22 @@ import cats.parse.Parser0 as P0
33
import cats.parse.Caret
44
import cats.parse.Rfc5234.{sp, alpha, digit, crlf, lf}
55

6+
import cats.syntax.all.*
7+
8+
case class Operator(raw: Char)
9+
610
enum Expr[F[_]]:
7-
case Add(l: Expr[F], r: Expr[F])
8-
case Mul(l: Expr[F], r: Expr[F])
11+
case Add(l: Expr[F], r: Expr[F], operator: F[Operator])
12+
case Mul(l: Expr[F], r: Expr[F], operator: F[Operator])
913
case Name(value: F[String])
1014
case Lit(value: F[Int])
1115

1216
def map[G[_]](f: [A] => F[A] => G[A]): Expr[G] =
1317
this match
14-
case Lit(num) => Lit(f(num))
15-
case Name(num) => Name(f(num))
16-
case Add(e1, e2) => Add(e1.map(f), e2.map(f))
17-
case Mul(e1, e2) => Mul(e1.map(f), e2.map(f))
18+
case Lit(num) => Lit(f(num))
19+
case Name(num) => Name(f(num))
20+
case Add(e1, e2, o) => Add(e1.map(f), e2.map(f), f(o))
21+
case Mul(e1, e2, o) => Mul(e1.map(f), e2.map(f), f(o))
1822
end Expr
1923

2024
enum Statement[F[_]]:
@@ -65,17 +69,23 @@ object QuickmaffsParser:
6569
val litNum =
6670
withSpan(P.charsWhile(_.isDigit).map(_.toInt)).map(Expr.Lit.apply)
6771

68-
val LB = P.char('(').surroundedBy(sp.rep0)
69-
val RB = P.char(')').surroundedBy(sp.rep0)
70-
val PLUS = P.char('+').surroundedBy(sp.rep0)
71-
val MUL = P.char('*').surroundedBy(sp.rep0)
72-
val ASS = P.char('=').surroundedBy(sp.rep0)
73-
val SEMICOLON = P.char(';')
72+
inline def operator(ch: Char): P[WithSpan[Operator]] =
73+
withSpan(P.char(ch).as(Operator(ch))).surroundedBy(sp.rep0)
74+
75+
val LB = operator('(')
76+
val RB = operator(')')
77+
val PLUS = operator('+')
78+
val MUL = operator('*')
79+
val ASS = operator('=')
80+
val SEMICOLON = operator(';')
7481

7582
private val expr = P.recursive[Expr[WithSpan]] { recurse =>
7683

77-
inline def pair[F[_]](p: P[Expr[F]], sym: P0[Unit]) =
78-
(p <* sym) ~ p
84+
inline def pair[F[_]](
85+
p: P[Expr[F]],
86+
sym: P[F[Operator]]
87+
): P[(Expr[F], Expr[F], F[Operator])] =
88+
(p, sym, p).tupled.map { case (l, o, r) => (l, r, o) }
7989

8090
val e = recurse.surroundedBy(sp.rep0)
8191

project.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//> using scala "3.2.1"
2+
//> using repository "sonatype-s01:snapshots"
3+
//> using lib "tech.neander::langoustine-app::0.0.18+1-97948520-SNAPSHOT"
4+
//> using lib "org.typelevel::cats-parse::0.3.8"

0 commit comments

Comments
 (0)