- (easy) Enables metals-vscode to fire
textDocument/*
- Adjust the LSP request position to the actual source position (generated by almond / ammonite)
- How to know the URI of generated Scala file for the ipynb? Can we get that information from running Kernel somehow?
- How to map "uri of notebook cell" + "position inside the cell" to the position of generated Scala source file?
According to notebookCellTextDocumentFilter of LSP 3.17.
We can enable vscode to fire textDocument/*
for all jupyter-notebook, where the Cell's language is Scala.
diff --git a/src/extension.ts b/src/extension.ts
index 13b49f6f..32a99c36 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -291,6 +291,10 @@ function launchMetals(
{ scheme: "file", language: "java" },
{ scheme: "jar", language: "scala" },
{ scheme: "jar", language: "java" },
+ {
+ notebook: { scheme: "file", notebookType: "jupyter-notebook" },
+ language: "scala",
+ },
],
synchronize: {
configurationSection: "metals",
Like this:
[Trace - 01:09:28 PM] Received request 'textDocument/completion - (140)'
Params: {
"context": {
"triggerKind": 2,
"triggerCharacter": "."
},
"textDocument": {
"uri": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W2sZmlsZQ%3D%3D"
},
"position": {
"line": 0,
"character": 2
}
}
[Trace - 00:04:24 AM] Received request 'textDocument/hover - (107)'
Params: {
"textDocument": {
"uri": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W1sZmlsZQ%3D%3D"
},
"position": {
"line": 0,
"character": 8
},
"range": {
"start": {
"line": 0,
"character": 8
},
"end": {
"line": 0,
"character": 8
}
}
}
We can find the
uri
is like"vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W1sZmlsZQ%3D%3D"
that point to a specific cell in the notebookUntitled-1.ipynb
- fragment hash is the
id
of the cell (we can find it from*.ipynb
file)
- fragment hash is the
position
is the position inside the cell
The problems here are
- Each cell is not compilable, in order to properly analyze the notebook, we have to combine the notebook cells into a valid form.
- Actually almond / Ammonite do that as Alex mentioned
- Metals have to map the "uri of the cell" and "position in the cell" into the position in the combined Scala source.
- It seems pretty tricky
For combining the cells, LSP 3.17 provide a notebookDocumentSync
feature (but I don't think it works for Metals).
The following change in scalameta/metals
enables client to fire notebookDocument/did*
(like didOpen
, didChange
and didClose
)
change in scalameta/metals
diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala
index 00aa1d791f..35b0ea2dc9 100644
--- a/metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala
+++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala
@@ -860,6 +860,16 @@ class MetalsLanguageServer(
ServerCommands.all.map(_.id).asJava
)
)
+ val selector = new NotebookSelector()
+ selector.setNotebook(
+ JEither.forLeft[String, NotebookDocumentFilter]("*")
+ )
+ selector.setCells(List(new NotebookSelectorCell("scala")).asJava)
+ capabilities.setNotebookDocumentSync(
+ new NotebookDocumentSyncRegistrationOptions(
+ List(selector).asJava
+ )
+ )
capabilities.setFoldingRangeProvider(true)
capabilities.setSelectionRangeProvider(true)
capabilities.setCodeLensProvider(new CodeLensOptions(false))
Those notifications send the content of the notebooks, and enable LSP servers track the content of notebooks
Example of didOpen
[Trace - 09:07:44 PM] Received notification 'notebookDocument/didOpen'
Params: {
"notebookDocument": {
"uri": "file:///Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb",
"notebookType": "jupyter-notebook",
"version": 0,
"cells": [
{
"kind": 2,
"document": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W0sZmlsZQ%3D%3D",
"metadata": {
"custom": {
"metadata": {}
}
}
},
{
"kind": 2,
"document": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W1sZmlsZQ%3D%3D",
"metadata": {
"custom": {
"metadata": {}
}
}
},
{
"kind": 2,
"document": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W2sZmlsZQ%3D%3D",
"metadata": {
"custom": {
"metadata": {}
}
}
}
],
"metadata": {
"custom": {
"cells": [],
"metadata": {
"kernelspec": {
"display_name": "Scala 2.13",
"language": "scala",
"name": "scala213"
},
"language_info": {
"codemirror_mode": "text/x-scala",
"file_extension": ".sc",
"mimetype": "text/x-scala",
"name": "scala",
"nbconvert_exporter": "script",
"version": "2.13.8"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
},
"indentAmount": " "
}
},
"cellTextDocuments": [
{
"uri": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W0sZmlsZQ%3D%3D",
"languageId": "scala",
"version": 1,
"text": "val x \u003d 1"
},
{
"uri": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W1sZmlsZQ%3D%3D",
"languageId": "scala",
"version": 1,
"text": "println(x)"
},
{
"uri": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W2sZmlsZQ%3D%3D",
"languageId": "scala",
"version": 1,
"text": ""
}
]
}
[Trace - 09:08:25 PM] Received notification 'notebookDocument/didChange'
Params: {
"notebookDocument": {
"version": 0,
"uri": "file:///Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb"
},
"change": {
"cells": {
"textContent": [
{
"document": {
"uri": "vscode-notebook-cell:/Users/tanishiking/src/github.com/tanishiking/scala3-playground/src/main/scala/Untitled-1.ipynb#W2sZmlsZQ%3D%3D",
"version": 2
},
"changes": [
{
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 0,
"character": 0
}
},
"rangeLength": 0,
"text": "x"
}
]
}
]
}
}
}
However, it doesn't work for Metals for 2 reasons
- Metals expect to have a file in the filesystem
- Even we can track the content in memory, Metals basically treat the files on the filesytem as a ground truth
- Being able to track the cell content doesn't make any sense with Almond / Ammonite
- Because what Metals should compile is the converted Scala source file by Ammonite, tracking cell content doesn't help
We should do the same thing as ScalaCli and Ammonite Script (though notebook would be even tricker)
- withPCAdjustLSP adjust the positions of the given file for presentation compiler.
- sourceAdjustments
- calls
sourceMapper.pcMapping
- calls
- sourceMapper.pcMapping
- Adjust source based heuristics (for build.sbt and worksheet) or BSP
- for BSP, it updates the content with BuildTargets.mappedTo that lookup
actualSources
using the givenuri
- may return TargetDate.MappedSource
- Who set
actualSources
?
sourceAdjustment for (SemanticDB) indexing should work if we properly registered the MappedSource
for ipynb
- We need to convert ipynb into a Ammonite generated Scala source, how can we map it?
- It might be needed to run build server (by whom???) to map the ipynb URI to generated Scala source's URI.
- Can we talk with running Kernel to get the URI?
- How to convert the position in the cell, into a position in the generated Scala source?
- not sure...