- For fun
- For your proprietary DSL
- So that you have a talk topic
- Write your own frontend?
- Monaco?
- Existing editor?
- Just syntax
- "Best effort" semantic analysis
- Full-blown analysis
We'll use Monaco and communicate with a compiler.
- Each editor has its own extension API
- Editors may require special environments (e.g. JVM or Node)
- Process boundaries must sometimes be crossed
┌──────────┐ ┌───────────┐
│ Your IDE │ ──> │ Your lang │
└──────────┘ └───────────┘
┌─────────┐ ┌──────────────────────┐ ┌──────────┐
│ │ ──> │ Scala │ <── │ │
│ │ └──────────────────────┘ │ │
│ │ ∧ │ │
│ │ │ │ │
│ │ │ │ │
│ │ ┌────────────┐ │ │ │
│ VS Code │ ──> │ Your lang │ <─────┼────── │ Your IDE │
│ │ └────────────┘ │ │ │
│ │ ∧ │ │ │
│ │ │ │ │ │
│ │ │ │ │ │
│ │ ┌──────┐ │ │ │ │
│ │ ──> │ Rust │ <┼──────────┼────── │ │
└─────────┘ └──────┘ │ │ └──────────┘
∧ │ │
│ │ │
│ │ │
┌───────────────────────────┐
│ Neovim │
└───────────────────────────┘
┌──────────┐ ┌───────────┐ ┌───────────┐
│ VS Code │ ──> │ │ ──> │ Scala │
└──────────┘ │ │ └───────────┘
┌──────────┐ │ │ ┌───────────┐
│ Your IDE │ ──> │ Something │ ──> │ Rust │
└──────────┘ │ │ └───────────┘
┌──────────┐ │ │ ┌───────────┐
│ Neovim │ ──> │ │ ──> │ Your lang │
└──────────┘ └───────────┘ └───────────┘
This is LSP!
┌────────┐ initialize request ┌────────┐
│ Client │ ─────────────────────> │ Server │
└────────┘ └────────┘
∧ initialize response │
└────────────────────────────┘
┌────────┐ textDocument/didOpen notification ┌────────┐
│ │ ─────────────────────────────────────> │ │
│ │ │ │
│ │ textDocument/didChange notification │ │
│ │ ─────────────────────────────────────> │ │
│ │ │ │
│ │ textDocument/didChange notification │ │
│ │ ─────────────────────────────────────> │ │
│ Client │ │ Server │
│ │ ... │ │
│ │ ─────────────────────────────────────> │ │
│ │ │ │
│ │ textDocument/didClose notification │ │
│ │ ─────────────────────────────────────> │ │
│ │ │ │
│ │ window/showMessage notification │ │
│ │ <───────────────────────────────────── │ │
└────────┘ └────────┘
┌────────┐ textDocument/definition request ┌────────┐
│ │ ──────────────────────────────────> │ │
│ │ │ │
│ │ textDocument/definition response │ │
│ │ <────────────────────────────────── │ │
│ │ │ │
│ │ textDocument/diagnostic request │ │
│ │ ──────────────────────────────────> │ │
│ Client │ │ Server │
│ │ textDocument/diagnostic response │ │
│ │ <────────────────────────────────── │ │
│ │ │ │
│ │ workspace/configuration request │ │
│ │ <────────────────────────────────── │ │
│ │ │ │
│ │ workspace/configuration response │ │
│ │ ──────────────────────────────────> │ │
└────────┘ └────────┘
{
"jsonrpc": "2.0",
"id": 2,
"method": "textDocument/definition",
"params": {
"textDocument": { "uri": "file:///workspace/demo.bad" },
"position": { "line": 5, "character": 5 }
}
}
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"uri": "file:///workspace/demo.bad",
"range": {
"start": { "line": 2, "character": 4 },
"end": { "line": 2, "character": 5 }
}
}
}
Code: kubukoz/badlang @ smol
branch (full version at main
)
textDocument/definition
: (URI, position) => (URI, range)
What we need to do:
- Load and parse the file at
uri
- Find out what node in the tree the cursor (
position
) is at - Ensure that node is a reference
- Find the first appearance of the referenced variable
- Return the variable's range
- Slides: this file
- Code