feat: add an e2e smoke testing (#114)

* feat: e2e smoke testing

* feat: redact value

* dev: don't compile in tests

* dev: use correct path to binary
This commit is contained in:
Myriad-Dreamin 2024-03-28 21:34:56 +08:00 committed by GitHub
parent d4dda9e06f
commit 5f27135419
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 943 additions and 7 deletions

View file

@ -116,6 +116,12 @@ jobs:
run: |
cargo build --release -p tinymist --target ${{ matrix.rust-target }}
if: startsWith(github.ref, 'refs/tags/') || matrix.regular_build == 'true'
- name: Copy binary to output directory
if: (startsWith(github.ref, 'refs/tags/') || matrix.regular_build == 'true')
shell: pwsh
run: |
cp "target/${{ matrix.rust-target }}/release/tinymist$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" "editors/vscode/out/"
cp "target/${{ matrix.rust-target }}/release/tinymist$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" "tinymist-${{ env.target }}$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )"
- name: Test tinymist
run: |
cargo test --release --workspace --target ${{ matrix.rust-target }}
@ -155,12 +161,6 @@ jobs:
with:
name: tinymist-${{ env.target }}.dwarf
path: target/${{ matrix.rust-target }}/release/tinymist-${{ env.target }}.dwarf
- name: Copy binary to output directory
if: (startsWith(github.ref, 'refs/tags/') || matrix.regular_build == 'true')
shell: pwsh
run: |
cp "target/${{ matrix.rust-target }}/release/tinymist$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" "editors/vscode/out/"
cp "target/${{ matrix.rust-target }}/release/tinymist$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )" "tinymist-${{ env.target }}$(If ('${{ matrix.platform }}' -eq 'win32') { '.exe' } else { '' } )"
- name: Upload binary artifact
if: (startsWith(github.ref, 'refs/tags/') || matrix.regular_build == 'true')
uses: actions/upload-artifact@v4

13
Cargo.lock generated
View file

@ -3593,6 +3593,19 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "tests"
version = "0.11.1"
dependencies = [
"insta",
"lsp-server",
"lsp-types",
"reflexo",
"serde",
"serde_json",
"tinymist",
]
[[package]]
name = "thiserror"
version = "1.0.58"

View file

@ -11,7 +11,7 @@ rust-version = "1.74"
[workspace]
resolver = "2"
members = ["crates/*"]
members = ["crates/*", "tests"]
[workspace.dependencies]
@ -84,6 +84,9 @@ serde_json = "1"
divan = "0.1.14"
insta = { version = "1.36", features = ["glob"] }
tinymist = { path = "./crates/tinymist/" }
tinymist-query = { path = "./crates/tinymist-query/" }
[profile.dev.package.insta]
opt-level = 3
@ -127,3 +130,4 @@ typst-pdf = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "tin
# reflexo = { path = "../typst.ts/crates/reflexo/" }
# typst-ts-core = { path = "../typst.ts/core" }
# typst-ts-compiler = { path = "../typst.ts/compiler" }
# typstyle = { path = "../typstyle" }

18
tests/Cargo.toml Normal file
View file

@ -0,0 +1,18 @@
[package]
name = "tests"
version.workspace = true
edition = "2021"
publish = false
[[test]]
name = "tinymist-e2e-tests"
path = "e2e/main.rs"
[dev-dependencies]
tinymist.workspace = true
lsp-server.workspace = true
lsp-types.workspace = true
serde.workspace = true
serde_json.workspace = true
reflexo.workspace = true
insta.workspace = true

482
tests/e2e/main.rs Normal file
View file

@ -0,0 +1,482 @@
use std::{
collections::HashSet,
hash::Hash,
io,
path::{Path, PathBuf},
process::Command,
};
use lsp_server::RequestId;
use serde_json::{json, Value};
fn handle_io<T>(res: io::Result<T>) -> T {
match res {
Ok(status) => status,
Err(err) => panic!("Error: {}", err),
}
}
fn find_git_root() -> io::Result<PathBuf> {
while !PathBuf::from(".git").exists() {
if std::env::set_current_dir("..").is_err() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"Git root not found",
));
}
}
std::env::current_dir()
}
// fn exec<'a>(cmd: &str, args: impl IntoIterator<Item = &'a str>) -> ExitStatus
// { handle_io(Command::new(cmd).args(args).status())
// }
fn find_char_boundary(s: &str, i: usize) -> usize {
for j in -4..4 {
let k = i as i64 + j;
if k < 0 || k >= s.len() as i64 {
continue;
}
if s.is_char_boundary(k as usize) {
return k as usize;
}
}
panic!("char boundary not found");
}
fn exec_output<'a>(cmd: &str, args: impl IntoIterator<Item = &'a str>) -> Vec<u8> {
let output = handle_io(Command::new(cmd).args(args).output());
let err = output.stderr;
// if contains panic
let err = std::str::from_utf8(&err).unwrap();
let panic = err.find("panic");
if let Some(p) = panic {
// capture surrounding lines
let panic_prev = p.saturating_sub(1024);
let panic_next = (p + 10240).min(err.len());
// find char boundary
let panic_prev = find_char_boundary(err, panic_prev);
let panic_next = find_char_boundary(err, panic_next);
panic!(
"panic found in stderr logging: PANIC_BEGIN\n\n{}\n\nPANIC_END",
&err[panic_prev..panic_next]
);
}
output.stdout
}
struct ReplayBuilder {
id: i32,
messages: Vec<lsp_server::Message>,
}
impl ReplayBuilder {
fn request_(&mut self, method: String, req: Value) {
let id = RequestId::from(self.id);
self.id += 1;
self.messages
.push(lsp_server::Message::Request(lsp_server::Request::new(
id, method, req,
)));
}
fn request<R: lsp_types::request::Request>(&mut self, req: Value) {
self.request_(R::METHOD.to_owned(), req);
}
fn notify_(&mut self, method: String, params: Value) {
self.messages.push(lsp_server::Message::Notification(
lsp_server::Notification::new(method, params),
));
}
fn notify<N: lsp_types::notification::Notification>(&mut self, params: Value) {
self.notify_(N::METHOD.to_owned(), params);
}
}
fn fixture(o: &str, f: impl FnOnce(&mut Value)) -> Value {
// tests/fixtures/o.json
let content = std::fs::read_to_string(format!("tests/fixtures/{}.json", o)).unwrap();
let mut req = serde_json::from_str(&content).unwrap();
f(&mut req);
req
}
fn gen(root: &Path, f: impl FnOnce(&mut ReplayBuilder)) {
let mut builder = ReplayBuilder {
id: 1,
messages: Vec::new(),
};
f(&mut builder);
// mkdir
handle_io(std::fs::create_dir_all(root));
// open root/mirror.log
let mut log = std::fs::File::create(root.join("mirror.log")).unwrap();
for msg in builder.messages {
msg.write(&mut log).unwrap();
}
}
fn messages(output: Vec<u8>) -> Vec<lsp_server::Message> {
let mut output = std::io::BufReader::new(output.as_slice());
// read all messages
let mut messages = Vec::new();
while let Ok(Some(msg)) = lsp_server::Message::read(&mut output) {
// match msg
messages.push(msg);
}
messages
}
#[test]
fn e2e() {
std::env::set_var("RUST_BACKTRACE", "full");
let cwd = find_git_root().unwrap();
// assert!(exec("cargo", ["build", "--release", "--bin",
// "tinymist"]).success());
let root = cwd.join("target/e2e/tinymist");
gen(&root.join("vscode"), |srv| {
use lsp_types::notification::*;
use lsp_types::request::*;
use lsp_types::*;
let wp = root.join("vscode");
let wp_url = lsp_types::Url::from_directory_path(&wp).unwrap();
srv.request::<Initialize>(fixture("initialization/vscode", |v| {
v["rootUri"] = json!(wp_url);
v["rootPath"] = json!(wp);
v["workspaceFolders"] = json!([{
"uri": wp_url,
"name": "tinymist",
}]);
}));
srv.notify::<Initialized>(json!({}));
// open editions/base.log and readlines
let log = std::fs::read_to_string("tests/fixtures/editions/base.log").unwrap();
let log = log.trim().split('\n').collect::<Vec<_>>();
let mut uri_set = HashSet::new();
let mut uris = Vec::new();
for line in log {
let mut v: Value = serde_json::from_str(line).unwrap();
// discover range in contentChanges and construct signatureHelp
let mut range_seeds = vec![];
if let Some(content_changes) = v
.get_mut("params")
.and_then(|v| v.get_mut("contentChanges"))
{
for change in content_changes.as_array_mut().unwrap() {
let range = change.get("range");
if let Some(range) = range {
let range: Range = serde_json::from_value(range.clone()).unwrap();
range_seeds.push(range);
}
}
}
let uri_name = v["params"]["textDocument"]["uri"].as_str().unwrap();
let url_v = wp_url.join(uri_name).unwrap();
let uri = json!(url_v);
v["params"]["textDocument"]["uri"] = uri;
let method = v["method"].as_str().unwrap();
srv.notify_("textDocument/".to_owned() + method, v["params"].clone());
let mut request_at_loc = |loc: Position| {
let pos = TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: url_v.clone() },
position: loc,
};
srv.request::<SignatureHelpRequest>(json!(SignatureHelpParams {
context: None,
work_done_progress_params: Default::default(),
text_document_position_params: pos.clone(),
}));
srv.request::<HoverRequest>(json!(HoverParams {
work_done_progress_params: Default::default(),
text_document_position_params: pos.clone(),
}));
srv.request::<Completion>(json!(CompletionParams {
text_document_position: pos.clone(),
context: None,
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
}));
srv.request::<GotoDefinition>(json!(GotoDefinitionParams {
text_document_position_params: pos.clone(),
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
}));
srv.request::<References>(json!(ReferenceParams {
text_document_position: pos.clone(),
context: ReferenceContext {
include_declaration: false,
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
}));
};
let mut seed_at_loc = |loc: Position| {
for i in loc.character.saturating_sub(2)..loc.character + 2 {
request_at_loc(Position {
line: loc.line,
character: i,
});
}
for l_delta in -1i32..1i32 {
if l_delta == 0 {
continue;
}
let l = (loc.line as i32) + l_delta;
if l < 0 {
continue;
}
let l = l as u32;
for i in 0..3 {
request_at_loc(Position {
line: l,
character: i,
});
}
}
// 10..100
for l_delta in -20i32..20i32 {
if l_delta == 0 {
continue;
}
let l = (loc.line as i32) + l_delta * 5;
if l < 0 {
continue;
}
let l = l as u32;
request_at_loc(Position {
line: l,
character: 0,
});
request_at_loc(Position {
line: l,
character: 2,
});
}
};
for r in range_seeds {
seed_at_loc(r.start);
seed_at_loc(r.end);
}
if uri_set.insert(url_v.clone()) {
uris.push(url_v);
}
const MI_POS: Position = Position {
line: 0,
character: 0,
};
const MX_POS: Position = Position {
line: u32::MAX / 1024,
character: u32::MAX / 1024,
};
for u in &uris {
srv.request::<FoldingRangeRequest>(json!(FoldingRangeParams {
text_document: TextDocumentIdentifier { uri: u.clone() },
work_done_progress_params: Default::default(),
partial_result_params: Default::default()
}));
srv.request::<DocumentSymbolRequest>(json!(DocumentSymbolParams {
text_document: TextDocumentIdentifier { uri: u.clone() },
work_done_progress_params: Default::default(),
partial_result_params: Default::default()
}));
srv.request::<CodeLensRequest>(json!(CodeLensParams {
text_document: TextDocumentIdentifier { uri: u.clone() },
work_done_progress_params: Default::default(),
partial_result_params: Default::default()
}));
srv.request::<InlayHintRequest>(json!(InlayHintParams {
text_document: TextDocumentIdentifier { uri: u.clone() },
work_done_progress_params: Default::default(),
range: Range {
start: MI_POS,
end: MX_POS
}
}));
// todo: specific test
// srv.request::<SemanticTokensFullRequest>(json!
// (SemanticTokensParams { text_document:
// TextDocumentIdentifier { uri: u.clone() },
// work_done_progress_params: Default::default(),
// partial_result_params: Default::default(),
// }));
}
}
});
let tinymist_binary = if cfg!(windows) {
cwd.join("editors/vscode/out/tinymist.exe")
} else {
cwd.join("editors/vscode/out/tinymist")
};
let tinymist_binary = tinymist_binary.to_str().unwrap();
let log_file = root.join("vscode/mirror.log").to_str().unwrap().to_owned();
let mut res = messages(exec_output(tinymist_binary, ["lsp", "--replay", &log_file]));
// retain not notification
res.retain(|msg| matches!(msg, lsp_server::Message::Response(_)));
// sort by id
res.sort_by_key(|msg| match msg {
lsp_server::Message::Request(req) => req.id.clone(),
lsp_server::Message::Response(res) => res.id.clone(),
lsp_server::Message::Notification(_) => RequestId::from(0),
});
// print to result.log
let res = serde_json::to_value(&res).unwrap();
let c = serde_json::to_string_pretty(&res).unwrap();
std::fs::write(root.join("vscode/result.json"), c).unwrap();
// let sorted_res
let sorted_res = sort_and_redact_value(res);
let c = serde_json::to_string_pretty(&sorted_res).unwrap();
let hash = reflexo::hash::hash128(&c);
std::fs::write(root.join("vscode/result_sorted.json"), c).unwrap();
let hash = format!("siphash128_13:{:x}", hash);
insta::assert_snapshot!(hash, @"siphash128_13:3ca43097c9772ff12a1918d876cbf6ad");
}
struct StableHash<'a>(&'a Value);
impl<'a> Hash for StableHash<'a> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self.0 {
Value::Null => 0.hash(state),
Value::Bool(b) => b.hash(state),
Value::Number(n) => {
if let Some(n) = n.as_i64() {
n.hash(state);
} else if let Some(n) = n.as_u64() {
n.hash(state);
} else if let Some(n) = n.as_f64() {
n.to_bits().hash(state);
} else {
panic!("unexpected number type");
}
}
Value::String(s) => s.hash(state),
Value::Array(a) => {
let mut a = a.clone();
a.sort_by(json_cmp);
a.len().hash(state);
for v in a {
StableHash(&v).hash(state);
}
}
Value::Object(o) => {
let mut keys = o.keys().collect::<Vec<_>>();
keys.sort();
keys.len().hash(state);
for k in keys {
k.hash(state);
StableHash(&o[k]).hash(state);
}
}
}
}
}
fn sort_and_redact_value(v: Value) -> Value {
match v {
Value::Null => Value::Null,
Value::Bool(b) => Value::Bool(b),
Value::Number(n) => Value::Number(n),
Value::String(s) => Value::String(s),
Value::Array(a) => {
let mut a = a;
a.sort_by(json_cmp);
Value::Array(a.into_iter().map(sort_and_redact_value).collect())
}
Value::Object(o) => {
let mut keys = o.keys().collect::<Vec<_>>();
keys.sort();
Value::Object(
keys.into_iter()
.map(|k| {
(k.clone(), {
let v = &o[k];
if k == "uri" || k == "targetUri" {
// get uri and set as file name
let uri = v.as_str().unwrap();
let uri = lsp_types::Url::parse(uri).unwrap();
let path = uri.to_file_path().unwrap();
let path = path.file_name().unwrap().to_str().unwrap();
Value::String(path.to_owned())
} else {
sort_and_redact_value(v.clone())
}
})
})
.collect(),
)
}
}
}
fn json_cmp(a: &Value, b: &Value) -> std::cmp::Ordering {
match (a, b) {
(Value::Null, Value::Null) => std::cmp::Ordering::Equal,
(Value::Bool(a), Value::Bool(b)) => a.cmp(b),
(Value::Number(a), Value::Number(b)) => {
if let (Some(a), Some(b)) = (a.as_i64(), b.as_i64()) {
a.cmp(&b)
} else if let (Some(a), Some(b)) = (a.as_u64(), b.as_u64()) {
a.cmp(&b)
} else if let (Some(a), Some(b)) = (a.as_f64(), b.as_f64()) {
a.partial_cmp(&b).unwrap()
} else {
panic!("unexpected number type");
}
}
(Value::String(a), Value::String(b)) => a.cmp(b),
(Value::Array(a), Value::Array(b)) => {
let mut a = a.clone();
let mut b = b.clone();
if a.len() != b.len() {
return a.len().cmp(&b.len());
}
a.sort_by(json_cmp);
b.sort_by(json_cmp);
for (a, b) in a.iter().zip(b.iter()) {
let cmp = json_cmp(a, b);
if cmp != std::cmp::Ordering::Equal {
return cmp;
}
}
std::cmp::Ordering::Equal
}
(Value::Object(a), Value::Object(b)) => {
let mut keys_a = a.keys().collect::<Vec<_>>();
let mut keys_b = b.keys().collect::<Vec<_>>();
keys_a.sort();
keys_b.sort();
if keys_a != keys_b {
return keys_a.cmp(&keys_b);
}
for k in keys_a {
let cmp = json_cmp(&a[k], &b[k]);
if cmp != std::cmp::Ordering::Equal {
return cmp;
}
}
std::cmp::Ordering::Equal
}
_ => std::cmp::Ordering::Equal,
}
}

141
tests/fixtures/editions/base.log vendored Normal file
View file

@ -0,0 +1,141 @@
{"method":"didOpen","params":{"textDocument":{"uri":"test.typ","languageId":"typst","version":1,"text":"#import \"lib.typ\"\r\n#import \"lib2.typ\": *\r\n\r\n#let f(x, y) = 1;\r\n\r\n#let g() = { }\r\n"}}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":2},"contentChanges":[{"range":{"start":{"line":5,"character":12},"end":{"line":5,"character":12}},"rangeLength":0,"text":"\r\n \r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":3},"contentChanges":[{"range":{"start":{"line":6,"character":2},"end":{"line":6,"character":2}},"rangeLength":0,"text":"f"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":4},"contentChanges":[{"range":{"start":{"line":6,"character":3},"end":{"line":6,"character":3}},"rangeLength":0,"text":"()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":5},"contentChanges":[{"range":{"start":{"line":6,"character":4},"end":{"line":6,"character":4}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":6},"contentChanges":[{"range":{"start":{"line":6,"character":5},"end":{"line":6,"character":5}},"rangeLength":0,"text":","}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":7},"contentChanges":[{"range":{"start":{"line":6,"character":6},"end":{"line":6,"character":6}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":8},"contentChanges":[{"range":{"start":{"line":6,"character":7},"end":{"line":6,"character":7}},"rangeLength":0,"text":"2"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":9},"contentChanges":[{"range":{"start":{"line":6,"character":9},"end":{"line":6,"character":9}},"rangeLength":0,"text":";"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":10},"contentChanges":[{"range":{"start":{"line":6,"character":10},"end":{"line":6,"character":10}},"rangeLength":0,"text":"\r\n "}]}}
{"method":"didOpen","params":{"textDocument":{"uri":"lib2.typ","languageId":"typst","version":1,"text":"\r\n#let l1(a, b) = 1;\r\n#let l2(c, d: none) = 2;\r\n\r\n"}}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":11},"contentChanges":[{"range":{"start":{"line":7,"character":2},"end":{"line":7,"character":2}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":12},"contentChanges":[{"range":{"start":{"line":7,"character":3},"end":{"line":7,"character":3}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":13},"contentChanges":[{"range":{"start":{"line":7,"character":4},"end":{"line":7,"character":4}},"rangeLength":0,"text":"()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":14},"contentChanges":[{"range":{"start":{"line":7,"character":5},"end":{"line":7,"character":5}},"rangeLength":0,"text":"0"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":15},"contentChanges":[{"range":{"start":{"line":7,"character":7},"end":{"line":7,"character":7}},"rangeLength":0,"text":";"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":16},"contentChanges":[{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":6}},"rangeLength":0,"text":","}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":17},"contentChanges":[{"range":{"start":{"line":7,"character":7},"end":{"line":7,"character":7}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":18},"contentChanges":[{"range":{"start":{"line":7,"character":8},"end":{"line":7,"character":8}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":19},"contentChanges":[{"range":{"start":{"line":8,"character":0},"end":{"line":8,"character":1}},"rangeLength":1,"text":""},{"range":{"start":{"line":7,"character":10},"end":{"line":7,"character":11}},"rangeLength":1,"text":""},{"range":{"start":{"line":6,"character":9},"end":{"line":6,"character":10}},"rangeLength":1,"text":""}]}}
{"method":"didSave","params":{"textDocument":{"uri":"test.typ"}}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":20},"contentChanges":[{"range":{"start":{"line":8,"character":1},"end":{"line":8,"character":1}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":21},"contentChanges":[{"range":{"start":{"line":9,"character":0},"end":{"line":9,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":22},"contentChanges":[{"range":{"start":{"line":10,"character":0},"end":{"line":10,"character":0}},"rangeLength":0,"text":"#"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":23},"contentChanges":[{"range":{"start":{"line":10,"character":1},"end":{"line":10,"character":1}},"rangeLength":0,"text":"f"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":24},"contentChanges":[{"range":{"start":{"line":10,"character":2},"end":{"line":10,"character":2}},"rangeLength":0,"text":"()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":25},"contentChanges":[{"range":{"start":{"line":10,"character":3},"end":{"line":10,"character":3}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":26},"contentChanges":[{"range":{"start":{"line":10,"character":4},"end":{"line":10,"character":4}},"rangeLength":0,"text":","}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":27},"contentChanges":[{"range":{"start":{"line":10,"character":5},"end":{"line":10,"character":5}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":28},"contentChanges":[{"range":{"start":{"line":10,"character":6},"end":{"line":10,"character":6}},"rangeLength":0,"text":"2"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":29},"contentChanges":[{"range":{"start":{"line":10,"character":8},"end":{"line":10,"character":8}},"rangeLength":0,"text":";"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":30},"contentChanges":[{"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":31},"contentChanges":[{"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":32},"contentChanges":[{"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":0}},"rangeLength":0,"text":"#"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":33},"contentChanges":[{"range":{"start":{"line":5,"character":1},"end":{"line":5,"character":1}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":34},"contentChanges":[{"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":2}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":35},"contentChanges":[{"range":{"start":{"line":5,"character":3},"end":{"line":5,"character":3}},"rangeLength":0,"text":"()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":36},"contentChanges":[{"range":{"start":{"line":5,"character":4},"end":{"line":5,"character":4}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":37},"contentChanges":[{"range":{"start":{"line":5,"character":5},"end":{"line":5,"character":5}},"rangeLength":0,"text":","}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":38},"contentChanges":[{"range":{"start":{"line":5,"character":6},"end":{"line":5,"character":6}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":39},"contentChanges":[{"range":{"start":{"line":5,"character":7},"end":{"line":5,"character":7}},"rangeLength":0,"text":"2"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":40},"contentChanges":[{"range":{"start":{"line":5,"character":9},"end":{"line":5,"character":9}},"rangeLength":0,"text":";"}]}}
{"method":"didSave","params":{"textDocument":{"uri":"test.typ"}}}
{"method":"didChange","params":{"textDocument":{"uri":"lib2.typ","version":2},"contentChanges":[{"range":{"start":{"line":2,"character":24},"end":{"line":2,"character":24}},"rangeLength":0,"text":"\r\n#let l2(c, d: none) = 2;"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"lib2.typ","version":3},"contentChanges":[{"range":{"start":{"line":3,"character":6},"end":{"line":3,"character":7}},"rangeLength":1,"text":"3"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"lib2.typ","version":4},"contentChanges":[{"range":{"start":{"line":3,"character":8},"end":{"line":3,"character":9}},"rangeLength":1,"text":"e"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"lib2.typ","version":5},"contentChanges":[{"range":{"start":{"line":3,"character":11},"end":{"line":3,"character":12}},"rangeLength":1,"text":"f"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":41},"contentChanges":[{"range":{"start":{"line":12,"character":9},"end":{"line":12,"character":9}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":42},"contentChanges":[{"range":{"start":{"line":13,"character":0},"end":{"line":13,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":43},"contentChanges":[{"range":{"start":{"line":14,"character":0},"end":{"line":14,"character":0}},"rangeLength":0,"text":"#"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":44},"contentChanges":[{"range":{"start":{"line":14,"character":1},"end":{"line":14,"character":1}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":45},"contentChanges":[{"range":{"start":{"line":14,"character":2},"end":{"line":14,"character":2}},"rangeLength":0,"text":"3"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":46},"contentChanges":[{"range":{"start":{"line":14,"character":3},"end":{"line":14,"character":3}},"rangeLength":0,"text":"()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":47},"contentChanges":[{"range":{"start":{"line":14,"character":4},"end":{"line":14,"character":4}},"rangeLength":0,"text":"4"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":48},"contentChanges":[{"range":{"start":{"line":14,"character":5},"end":{"line":14,"character":5}},"rangeLength":0,"text":","}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":49},"contentChanges":[{"range":{"start":{"line":14,"character":6},"end":{"line":14,"character":6}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":50},"contentChanges":[{"range":{"start":{"line":14,"character":7},"end":{"line":14,"character":7}},"rangeLength":0,"text":"5"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":51},"contentChanges":[{"range":{"start":{"line":14,"character":9},"end":{"line":14,"character":9}},"rangeLength":0,"text":";"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":52},"contentChanges":[{"range":{"start":{"line":14,"character":7},"end":{"line":14,"character":7}},"rangeLength":0,"text":"f"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":53},"contentChanges":[{"range":{"start":{"line":14,"character":8},"end":{"line":14,"character":8}},"rangeLength":0,"text":":"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":54},"contentChanges":[{"range":{"start":{"line":14,"character":9},"end":{"line":14,"character":9}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":55},"contentChanges":[{"range":{"start":{"line":4,"character":0},"end":{"line":6,"character":0}},"rangeLength":14,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":56},"contentChanges":[{"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":57},"contentChanges":[{"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":58},"contentChanges":[{"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":0}},"rangeLength":0,"text":"#"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":59},"contentChanges":[{"range":{"start":{"line":5,"character":1},"end":{"line":5,"character":1}},"rangeLength":0,"text":"f"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":60},"contentChanges":[{"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":2}},"rangeLength":0,"text":"()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":61},"contentChanges":[{"range":{"start":{"line":5,"character":0},"end":{"line":6,"character":0}},"rangeLength":6,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":62},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":4,"character":0}},"rangeLength":19,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":63},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":4,"character":0}},"rangeLength":2,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":64},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":65},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":0}},"rangeLength":0,"text":"#"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":66},"contentChanges":[{"range":{"start":{"line":3,"character":1},"end":{"line":3,"character":1}},"rangeLength":0,"text":"f"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":67},"contentChanges":[{"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":2}},"rangeLength":0,"text":"()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":68},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":4,"character":0}},"rangeLength":6,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":69},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":0}},"rangeLength":0,"text":"#f()\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":70},"contentChanges":[{"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":4}},"rangeLength":2,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":71},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":2}},"rangeLength":2,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":72},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":4,"character":0}},"rangeLength":2,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":73},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":74},"contentChanges":[{"range":{"start":{"line":3,"character":0},"end":{"line":3,"character":0}},"rangeLength":0,"text":"#let f(x, y) = 1;\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":75},"contentChanges":[{"range":{"start":{"line":5,"character":0},"end":{"line":6,"character":0}},"rangeLength":2,"text":""}]}}
{"method":"didSave","params":{"textDocument":{"uri":"test.typ"}}}
{"method":"didOpen","params":{"textDocument":{"uri":"lib.typ","languageId":"typst","version":1,"text":""}}}
{"method":"didChange","params":{"textDocument":{"uri":"lib.typ","version":2},"contentChanges":[{"range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}},"rangeLength":0,"text":"\r\n#let l1(a, b) = 1;\r\n#let l2(c, d: none) = 2;\r\n#let l3(e, f: none) = 2;\r\n\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":76},"contentChanges":[{"range":{"start":{"line":3,"character":17},"end":{"line":3,"character":17}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":77},"contentChanges":[{"range":{"start":{"line":4,"character":0},"end":{"line":4,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":78},"contentChanges":[{"range":{"start":{"line":5,"character":0},"end":{"line":5,"character":0}},"rangeLength":0,"text":"#"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":79},"contentChanges":[{"range":{"start":{"line":5,"character":1},"end":{"line":5,"character":1}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":80},"contentChanges":[{"range":{"start":{"line":5,"character":2},"end":{"line":5,"character":2}},"rangeLength":0,"text":"i"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":81},"contentChanges":[{"range":{"start":{"line":5,"character":3},"end":{"line":5,"character":3}},"rangeLength":0,"text":"b"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":82},"contentChanges":[{"range":{"start":{"line":5,"character":4},"end":{"line":5,"character":4}},"rangeLength":0,"text":"."}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":83},"contentChanges":[{"range":{"start":{"line":5,"character":5},"end":{"line":5,"character":5}},"rangeLength":0,"text":"l1()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":84},"contentChanges":[{"range":{"start":{"line":5,"character":8},"end":{"line":5,"character":8}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":85},"contentChanges":[{"range":{"start":{"line":5,"character":9},"end":{"line":5,"character":9}},"rangeLength":0,"text":","}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":86},"contentChanges":[{"range":{"start":{"line":5,"character":10},"end":{"line":5,"character":10}},"rangeLength":0,"text":"2"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":87},"contentChanges":[{"range":{"start":{"line":5,"character":10},"end":{"line":5,"character":10}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":88},"contentChanges":[{"range":{"start":{"line":5,"character":13},"end":{"line":5,"character":13}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":89},"contentChanges":[{"range":{"start":{"line":6,"character":0},"end":{"line":6,"character":0}},"rangeLength":0,"text":"\r\n"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":90},"contentChanges":[{"range":{"start":{"line":7,"character":0},"end":{"line":7,"character":0}},"rangeLength":0,"text":"#"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":91},"contentChanges":[{"range":{"start":{"line":7,"character":1},"end":{"line":7,"character":1}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":92},"contentChanges":[{"range":{"start":{"line":7,"character":2},"end":{"line":7,"character":2}},"rangeLength":0,"text":"i"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":93},"contentChanges":[{"range":{"start":{"line":7,"character":3},"end":{"line":7,"character":3}},"rangeLength":0,"text":"b"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":94},"contentChanges":[{"range":{"start":{"line":7,"character":4},"end":{"line":7,"character":4}},"rangeLength":0,"text":"."}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":95},"contentChanges":[{"range":{"start":{"line":7,"character":5},"end":{"line":7,"character":5}},"rangeLength":0,"text":"l2()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":96},"contentChanges":[{"range":{"start":{"line":7,"character":8},"end":{"line":7,"character":8}},"rangeLength":0,"text":"3"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":97},"contentChanges":[{"range":{"start":{"line":7,"character":9},"end":{"line":7,"character":9}},"rangeLength":0,"text":","}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":98},"contentChanges":[{"range":{"start":{"line":7,"character":10},"end":{"line":7,"character":10}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":99},"contentChanges":[{"range":{"start":{"line":7,"character":11},"end":{"line":7,"character":11}},"rangeLength":0,"text":"4"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":100},"contentChanges":[{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"rangeLength":0,"text":";"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":101},"contentChanges":[{"range":{"start":{"line":7,"character":11},"end":{"line":7,"character":11}},"rangeLength":0,"text":"d"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":102},"contentChanges":[{"range":{"start":{"line":7,"character":12},"end":{"line":7,"character":12}},"rangeLength":0,"text":":"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":103},"contentChanges":[{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":104},"contentChanges":[{"range":{"start":{"line":11,"character":10},"end":{"line":11,"character":10}},"rangeLength":0,"text":"\r\n "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":105},"contentChanges":[{"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":2}},"rangeLength":0,"text":"#"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":106},"contentChanges":[{"range":{"start":{"line":12,"character":3},"end":{"line":12,"character":3}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":107},"contentChanges":[{"range":{"start":{"line":12,"character":4},"end":{"line":12,"character":4}},"rangeLength":0,"text":"i"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":108},"contentChanges":[{"range":{"start":{"line":12,"character":5},"end":{"line":12,"character":5}},"rangeLength":0,"text":"b"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":109},"contentChanges":[{"range":{"start":{"line":12,"character":6},"end":{"line":12,"character":6}},"rangeLength":0,"text":"."}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":110},"contentChanges":[{"range":{"start":{"line":12,"character":6},"end":{"line":12,"character":7}},"rangeLength":1,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":111},"contentChanges":[{"range":{"start":{"line":12,"character":3},"end":{"line":12,"character":6}},"rangeLength":3,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":112},"contentChanges":[{"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":3}},"rangeLength":1,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":113},"contentChanges":[{"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":2}},"rangeLength":0,"text":"b"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":114},"contentChanges":[{"range":{"start":{"line":12,"character":3},"end":{"line":12,"character":3}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":115},"contentChanges":[{"range":{"start":{"line":12,"character":4},"end":{"line":12,"character":4}},"rangeLength":0,"text":"i"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":116},"contentChanges":[{"range":{"start":{"line":12,"character":4},"end":{"line":12,"character":5}},"rangeLength":1,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":117},"contentChanges":[{"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":4}},"rangeLength":2,"text":""}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":118},"contentChanges":[{"range":{"start":{"line":12,"character":2},"end":{"line":12,"character":2}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":119},"contentChanges":[{"range":{"start":{"line":12,"character":3},"end":{"line":12,"character":3}},"rangeLength":0,"text":"i"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":120},"contentChanges":[{"range":{"start":{"line":12,"character":4},"end":{"line":12,"character":4}},"rangeLength":0,"text":"b"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":121},"contentChanges":[{"range":{"start":{"line":12,"character":5},"end":{"line":12,"character":5}},"rangeLength":0,"text":"."}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":122},"contentChanges":[{"range":{"start":{"line":12,"character":6},"end":{"line":12,"character":6}},"rangeLength":0,"text":"l"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":123},"contentChanges":[{"range":{"start":{"line":12,"character":7},"end":{"line":12,"character":7}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":124},"contentChanges":[{"range":{"start":{"line":12,"character":8},"end":{"line":12,"character":8}},"rangeLength":0,"text":"()"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":125},"contentChanges":[{"range":{"start":{"line":12,"character":9},"end":{"line":12,"character":9}},"rangeLength":0,"text":"1"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":126},"contentChanges":[{"range":{"start":{"line":12,"character":10},"end":{"line":12,"character":10}},"rangeLength":0,"text":","}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":127},"contentChanges":[{"range":{"start":{"line":12,"character":11},"end":{"line":12,"character":11}},"rangeLength":0,"text":" "}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":128},"contentChanges":[{"range":{"start":{"line":12,"character":12},"end":{"line":12,"character":12}},"rangeLength":0,"text":"2"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":129},"contentChanges":[{"range":{"start":{"line":12,"character":14},"end":{"line":12,"character":14}},"rangeLength":0,"text":";"}]}}
{"method":"didChange","params":{"textDocument":{"uri":"test.typ","version":130},"contentChanges":[{"range":{"start":{"line":12,"character":14},"end":{"line":12,"character":15}},"rangeLength":1,"text":""}]}}
{"method":"didSave","params":{"textDocument":{"uri":"test.typ"}}}

View file

@ -0,0 +1,278 @@
{
"processId": 126420,
"clientInfo": { "name": "Visual Studio Code", "version": "1.87.2" },
"locale": "en",
"capabilities": {
"workspace": {
"applyEdit": true,
"workspaceEdit": {
"documentChanges": true,
"resourceOperations": ["create", "rename", "delete"],
"failureHandling": "textOnlyTransactional",
"normalizesLineEndings": true,
"changeAnnotationSupport": { "groupsOnLabel": true }
},
"configuration": true,
"didChangeWatchedFiles": {
"dynamicRegistration": true,
"relativePatternSupport": true
},
"symbol": {
"dynamicRegistration": true,
"symbolKind": {
"valueSet": [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26
]
},
"tagSupport": { "valueSet": [1] },
"resolveSupport": { "properties": ["location.range"] }
},
"codeLens": { "refreshSupport": true },
"executeCommand": { "dynamicRegistration": true },
"didChangeConfiguration": { "dynamicRegistration": true },
"workspaceFolders": true,
"foldingRange": { "refreshSupport": true },
"semanticTokens": { "refreshSupport": true },
"fileOperations": {
"dynamicRegistration": true,
"didCreate": true,
"didRename": true,
"didDelete": true,
"willCreate": true,
"willRename": true,
"willDelete": true
},
"inlineValue": { "refreshSupport": true },
"inlayHint": { "refreshSupport": true },
"diagnostics": { "refreshSupport": true }
},
"textDocument": {
"publishDiagnostics": {
"relatedInformation": true,
"versionSupport": false,
"tagSupport": { "valueSet": [1, 2] },
"codeDescriptionSupport": true,
"dataSupport": true
},
"synchronization": {
"dynamicRegistration": true,
"willSave": true,
"willSaveWaitUntil": true,
"didSave": true
},
"completion": {
"dynamicRegistration": true,
"contextSupport": true,
"completionItem": {
"snippetSupport": true,
"commitCharactersSupport": true,
"documentationFormat": ["markdown", "plaintext"],
"deprecatedSupport": true,
"preselectSupport": true,
"tagSupport": { "valueSet": [1] },
"insertReplaceSupport": true,
"resolveSupport": {
"properties": ["documentation", "detail", "additionalTextEdits"]
},
"insertTextModeSupport": { "valueSet": [1, 2] },
"labelDetailsSupport": true
},
"insertTextMode": 2,
"completionItemKind": {
"valueSet": [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25
]
},
"completionList": {
"itemDefaults": [
"commitCharacters",
"editRange",
"insertTextFormat",
"insertTextMode",
"data"
]
}
},
"hover": {
"dynamicRegistration": true,
"contentFormat": ["markdown", "plaintext"]
},
"signatureHelp": {
"dynamicRegistration": true,
"signatureInformation": {
"documentationFormat": ["markdown", "plaintext"],
"parameterInformation": { "labelOffsetSupport": true },
"activeParameterSupport": true
},
"contextSupport": true
},
"definition": { "dynamicRegistration": true, "linkSupport": true },
"references": { "dynamicRegistration": true },
"documentHighlight": { "dynamicRegistration": true },
"documentSymbol": {
"dynamicRegistration": true,
"symbolKind": {
"valueSet": [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26
]
},
"hierarchicalDocumentSymbolSupport": true,
"tagSupport": { "valueSet": [1] },
"labelSupport": true
},
"codeAction": {
"dynamicRegistration": true,
"isPreferredSupport": true,
"disabledSupport": true,
"dataSupport": true,
"resolveSupport": { "properties": ["edit"] },
"codeActionLiteralSupport": {
"codeActionKind": {
"valueSet": [
"",
"quickfix",
"refactor",
"refactor.extract",
"refactor.inline",
"refactor.rewrite",
"source",
"source.organizeImports"
]
}
},
"honorsChangeAnnotations": true
},
"codeLens": { "dynamicRegistration": true },
"formatting": { "dynamicRegistration": true },
"rangeFormatting": {
"dynamicRegistration": true,
"rangesSupport": true
},
"onTypeFormatting": { "dynamicRegistration": true },
"rename": {
"dynamicRegistration": true,
"prepareSupport": true,
"prepareSupportDefaultBehavior": 1,
"honorsChangeAnnotations": true
},
"documentLink": { "dynamicRegistration": true, "tooltipSupport": true },
"typeDefinition": { "dynamicRegistration": true, "linkSupport": true },
"implementation": { "dynamicRegistration": true, "linkSupport": true },
"colorProvider": { "dynamicRegistration": true },
"foldingRange": {
"dynamicRegistration": true,
"rangeLimit": 5000,
"lineFoldingOnly": true,
"foldingRangeKind": { "valueSet": ["comment", "imports", "region"] },
"foldingRange": { "collapsedText": false }
},
"declaration": { "dynamicRegistration": true, "linkSupport": true },
"selectionRange": { "dynamicRegistration": true },
"callHierarchy": { "dynamicRegistration": true },
"semanticTokens": {
"dynamicRegistration": true,
"tokenTypes": [
"namespace",
"type",
"class",
"enum",
"interface",
"struct",
"typeParameter",
"parameter",
"variable",
"property",
"enumMember",
"event",
"function",
"method",
"macro",
"keyword",
"modifier",
"comment",
"string",
"number",
"regexp",
"operator",
"decorator"
],
"tokenModifiers": [
"declaration",
"definition",
"readonly",
"static",
"deprecated",
"abstract",
"async",
"modification",
"documentation",
"defaultLibrary"
],
"formats": ["relative"],
"requests": { "range": true, "full": { "delta": true } },
"multilineTokenSupport": false,
"overlappingTokenSupport": false,
"serverCancelSupport": true,
"augmentsSyntaxTokens": true
},
"linkedEditingRange": { "dynamicRegistration": true },
"typeHierarchy": { "dynamicRegistration": true },
"inlineValue": { "dynamicRegistration": true },
"inlayHint": {
"dynamicRegistration": true,
"resolveSupport": {
"properties": [
"tooltip",
"textEdits",
"label.tooltip",
"label.location",
"label.command"
]
}
},
"diagnostic": {
"dynamicRegistration": true,
"relatedDocumentSupport": false
}
},
"window": {
"showMessage": {
"messageActionItem": { "additionalPropertiesSupport": true }
},
"showDocument": { "support": true },
"workDoneProgress": true
},
"general": {
"staleRequestSupport": {
"cancel": true,
"retryOnContentModified": [
"textDocument/semanticTokens/full",
"textDocument/semanticTokens/range",
"textDocument/semanticTokens/full/delta"
]
},
"regularExpressions": { "engine": "ECMAScript", "version": "ES2020" },
"markdown": { "parser": "marked", "version": "1.1.0" },
"positionEncodings": ["utf-16"]
},
"notebookDocument": {
"synchronization": {
"dynamicRegistration": true,
"executionSummarySupport": true
}
}
},
"initializationOptions": {
"outputPath": "$root/target/$dir/$name",
"exportPdf": "onSave",
"semanticTokens": "enable",
"noSystemFonts": null,
"typstExtraArgs": [],
"trace": { "server": "off" },
"experimentalFormatterMode": "disable"
},
"trace": "off"
}