mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 05:05:00 +00:00
fix: empty url sent from neovim (#130)
* fix: empty url sent from neovim * dev: move e2e build script * dev: make stronger name for empty urls * fix: work around empty url * fix: convert empty link on unix system
This commit is contained in:
parent
b76e80bad3
commit
edbb7bc1af
7 changed files with 311 additions and 46 deletions
|
@ -6,6 +6,7 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use lsp_types::{self, Url};
|
||||
use once_cell::sync::Lazy;
|
||||
use reflexo::path::PathClean;
|
||||
|
||||
pub type LspPosition = lsp_types::Position;
|
||||
|
@ -68,21 +69,35 @@ pub type TypstCompletion = crate::upstream::Completion;
|
|||
pub type TypstCompletionKind = crate::upstream::CompletionKind;
|
||||
|
||||
const UNTITLED_ROOT: &str = "/untitled";
|
||||
static EMPTY_URL: Lazy<Url> = Lazy::new(|| Url::parse("file://").unwrap());
|
||||
|
||||
pub fn path_to_url(path: &Path) -> anyhow::Result<Url> {
|
||||
if let Ok(untitled) = path.strip_prefix(UNTITLED_ROOT) {
|
||||
// rust-url will panic on converting an empty path.
|
||||
if untitled == Path::new("nEoViM-BuG") {
|
||||
return Ok(EMPTY_URL.clone());
|
||||
}
|
||||
|
||||
return Ok(Url::parse(&format!("untitled:{}", untitled.display()))?);
|
||||
}
|
||||
|
||||
Url::from_file_path(path).map_err(|e| {
|
||||
Url::from_file_path(path).or_else(|e| {
|
||||
let _: () = e;
|
||||
anyhow::anyhow!("could not convert path to URI: path: {path:?}",)
|
||||
|
||||
anyhow::bail!("could not convert path to URI: path: {path:?}",)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn url_to_path(uri: Url) -> PathBuf {
|
||||
if uri.scheme() == "file" {
|
||||
return uri.to_file_path().unwrap();
|
||||
// typst converts an empty path to `Path::new("/")`, which is undesirable.
|
||||
if !uri.has_host() && uri.path() == "/" {
|
||||
return PathBuf::from("/untitled/nEoViM-BuG");
|
||||
}
|
||||
|
||||
return uri
|
||||
.to_file_path()
|
||||
.unwrap_or_else(|_| panic!("could not convert URI to path: URI: {uri:?}",));
|
||||
}
|
||||
|
||||
if uri.scheme() == "untitled" {
|
||||
|
@ -375,6 +390,17 @@ mod test {
|
|||
assert_eq!(path, Path::new("/untitled/test").clean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unnamed_buffer() {
|
||||
// https://github.com/neovim/nvim-lspconfig/pull/2226
|
||||
let uri = EMPTY_URL.clone();
|
||||
let path = url_to_path(uri);
|
||||
assert_eq!(path, Path::new("/untitled/nEoViM-BuG"));
|
||||
|
||||
let uri2 = path_to_url(&path).unwrap();
|
||||
assert_eq!(EMPTY_URL.clone(), uri2);
|
||||
}
|
||||
|
||||
const ENCODING_TEST_STRING: &str = "test 🥺 test";
|
||||
|
||||
#[test]
|
||||
|
|
4
scripts/e2e.ps1
Normal file
4
scripts/e2e.ps1
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
cargo build --release --bin tinymist
|
||||
Copy-Item -Path ".\target\release\tinymist.exe" -Destination ".\editors\vscode\out\tinymist.exe" -Force
|
||||
cargo insta test -p tests --accept
|
3
scripts/e2e.sh
Executable file
3
scripts/e2e.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
cargo build --release --bin tinymist
|
||||
cp target/release/tinymist editors/vscode/out/tinymist
|
||||
cargo insta test -p tests --accept
|
|
@ -134,40 +134,32 @@ fn messages(output: Vec<u8>) -> Vec<lsp_server::Message> {
|
|||
messages
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn e2e() {
|
||||
std::env::set_var("RUST_BACKTRACE", "full");
|
||||
struct SmokeArgs {
|
||||
root: PathBuf,
|
||||
init: String,
|
||||
log: String,
|
||||
}
|
||||
|
||||
let cwd = find_git_root().unwrap();
|
||||
if cfg!(target_os = "...") {
|
||||
let w = Command::new("cargo")
|
||||
.args(["build", "--release", "--bin", "tinymist"])
|
||||
.status();
|
||||
assert!(handle_io(w).success());
|
||||
handle_io(std::fs::copy(
|
||||
cwd.join("target/release/tinymist.exe"),
|
||||
cwd.join("editors/vscode/out/tinymist.exe"),
|
||||
));
|
||||
}
|
||||
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);
|
||||
fn gen_smoke(args: SmokeArgs) {
|
||||
use lsp_types::notification::*;
|
||||
use lsp_types::request::*;
|
||||
use lsp_types::*;
|
||||
|
||||
let SmokeArgs { root, init, log } = args;
|
||||
gen(&root, |srv| {
|
||||
let root_uri = lsp_types::Url::from_directory_path(&root).unwrap();
|
||||
srv.request::<Initialize>(fixture(&init, |v| {
|
||||
v["rootUri"] = json!(root_uri);
|
||||
v["rootPath"] = json!(root);
|
||||
v["workspaceFolders"] = json!([{
|
||||
"uri": wp_url,
|
||||
"uri": root_uri,
|
||||
"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 = std::fs::read_to_string(&log).unwrap();
|
||||
let log = log.trim().split('\n').collect::<Vec<_>>();
|
||||
let mut uri_set = HashSet::new();
|
||||
let mut uris = Vec::new();
|
||||
|
@ -191,9 +183,12 @@ fn e2e() {
|
|||
}
|
||||
|
||||
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 url_v = if uri_name.starts_with("file:") || uri_name.starts_with("untitled:") {
|
||||
lsp_types::Url::parse(uri_name).unwrap()
|
||||
} else {
|
||||
root_uri.join(uri_name).unwrap()
|
||||
};
|
||||
v["params"]["textDocument"]["uri"] = json!(url_v);
|
||||
let method = v["method"].as_str().unwrap();
|
||||
srv.notify_("textDocument/".to_owned() + method, v["params"].clone());
|
||||
|
||||
|
@ -330,15 +325,12 @@ fn e2e() {
|
|||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let tinymist_binary = if cfg!(windows) {
|
||||
cwd.join("editors/vscode/out/tinymist.exe")
|
||||
} else {
|
||||
cwd.join("editors/vscode/out/tinymist")
|
||||
};
|
||||
fn replay_log(tinymist_binary: &Path, root: &Path) -> String {
|
||||
let tinymist_binary = tinymist_binary.to_str().unwrap();
|
||||
|
||||
let log_file = root.join("vscode/mirror.log").to_str().unwrap().to_owned();
|
||||
let log_file = root.join("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(_)));
|
||||
|
@ -351,15 +343,51 @@ fn e2e() {
|
|||
// 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();
|
||||
std::fs::write(root.join("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);
|
||||
std::fs::write(root.join("result_sorted.json"), c).unwrap();
|
||||
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:db9523369516f3a16997fc1913381d6e");
|
||||
format!("siphash128_13:{:x}", hash)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn e2e() {
|
||||
std::env::set_var("RUST_BACKTRACE", "full");
|
||||
|
||||
let cwd = find_git_root().unwrap();
|
||||
|
||||
let tinymist_binary = if cfg!(windows) {
|
||||
cwd.join("editors/vscode/out/tinymist.exe")
|
||||
} else {
|
||||
cwd.join("editors/vscode/out/tinymist")
|
||||
};
|
||||
|
||||
let root = cwd.join("target/e2e/tinymist");
|
||||
|
||||
{
|
||||
gen_smoke(SmokeArgs {
|
||||
root: root.join("neovim"),
|
||||
init: "initialization/neovim-0.9.4".to_owned(),
|
||||
log: "tests/fixtures/editions/neovim_unnamed_buffer.log".to_owned(),
|
||||
});
|
||||
|
||||
let hash = replay_log(&tinymist_binary, &root.join("neovim"));
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:ff13445227b3b86b70905bba912bcd0a");
|
||||
}
|
||||
|
||||
{
|
||||
gen_smoke(SmokeArgs {
|
||||
root: root.join("vscode"),
|
||||
init: "initialization/vscode-1.87.2".to_owned(),
|
||||
log: "tests/fixtures/editions/base.log".to_owned(),
|
||||
});
|
||||
|
||||
let hash = replay_log(&tinymist_binary, &root.join("vscode"));
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:db9523369516f3a16997fc1913381d6e");
|
||||
}
|
||||
}
|
||||
|
||||
struct StableHash<'a>(&'a Value);
|
||||
|
@ -424,10 +452,19 @@ fn sort_and_redact_value(v: Value) -> Value {
|
|||
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())
|
||||
if uri == "file://" || uri == "file:///" {
|
||||
Value::String("".to_owned())
|
||||
} else {
|
||||
let uri = lsp_types::Url::parse(uri).unwrap();
|
||||
|
||||
match uri.to_file_path() {
|
||||
Ok(path) => {
|
||||
let path = path.file_name().unwrap().to_str().unwrap();
|
||||
Value::String(path.to_owned())
|
||||
}
|
||||
Err(_) => Value::String(uri.to_string()),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sort_and_redact_value(v.clone())
|
||||
}
|
||||
|
|
21
tests/fixtures/editions/neovim_unnamed_buffer.log
vendored
Normal file
21
tests/fixtures/editions/neovim_unnamed_buffer.log
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{"method":"didOpen","params":{"textDocument":{"text":"\n","uri":"file://","version":0,"languageId":"typst"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":0},"start":{"line":0,"character":0}},"rangeLength":0,"text":"#"}],"textDocument":{"version":3,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":1},"start":{"line":0,"character":1}},"rangeLength":0,"text":"l"}],"textDocument":{"version":4,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":2},"start":{"line":0,"character":2}},"rangeLength":0,"text":"e"},{"range":{"end":{"line":0,"character":3},"start":{"line":0,"character":3}},"rangeLength":0,"text":"t"}],"textDocument":{"version":6,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":4},"start":{"line":0,"character":4}},"rangeLength":0,"text":" "}],"textDocument":{"version":7,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":5},"start":{"line":0,"character":5}},"rangeLength":0,"text":"a"}],"textDocument":{"version":8,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":6},"start":{"line":0,"character":6}},"rangeLength":0,"text":" "}],"textDocument":{"version":9,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":7},"start":{"line":0,"character":7}},"rangeLength":0,"text":"="}],"textDocument":{"version":10,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":8},"start":{"line":0,"character":8}},"rangeLength":0,"text":" "}],"textDocument":{"version":11,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":9},"start":{"line":0,"character":9}},"rangeLength":0,"text":"5"}],"textDocument":{"version":12,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":0,"character":10},"start":{"line":0,"character":10}},"rangeLength":0,"text":"\n"}],"textDocument":{"version":13,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":0},"start":{"line":1,"character":0}},"rangeLength":0,"text":"#"}],"textDocument":{"version":14,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":1},"start":{"line":1,"character":1}},"rangeLength":0,"text":"l"}],"textDocument":{"version":15,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":2},"start":{"line":1,"character":2}},"rangeLength":0,"text":"e"},{"range":{"end":{"line":1,"character":3},"start":{"line":1,"character":3}},"rangeLength":0,"text":"t"}],"textDocument":{"version":17,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":4},"start":{"line":1,"character":4}},"rangeLength":0,"text":" "}],"textDocument":{"version":18,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":5},"start":{"line":1,"character":5}},"rangeLength":0,"text":"f"},{"range":{"end":{"line":1,"character":6},"start":{"line":1,"character":6}},"rangeLength":0,"text":"u"}],"textDocument":{"version":20,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":7},"start":{"line":1,"character":7}},"rangeLength":0,"text":"n"}],"textDocument":{"version":21,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":8},"start":{"line":1,"character":8}},"rangeLength":0,"text":"c"}],"textDocument":{"version":22,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":9},"start":{"line":1,"character":9}},"rangeLength":0,"text":"("}],"textDocument":{"version":23,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":10},"start":{"line":1,"character":10}},"rangeLength":0,"text":")"}],"textDocument":{"version":24,"uri":"file://"}}}
|
||||
{"method":"didChange","params":{"contentChanges":[{"range":{"end":{"line":1,"character":10},"start":{"line":1,"character":10}},"rangeLength":0,"text":"a"}],"textDocument":{"version":25,"uri":"file://"}}}
|
174
tests/fixtures/initialization/neovim-0.9.4.json
vendored
Normal file
174
tests/fixtures/initialization/neovim-0.9.4.json
vendored
Normal file
|
@ -0,0 +1,174 @@
|
|||
{
|
||||
"processId": 3414686,
|
||||
"clientInfo": { "version": "0.9.4", "name": "Neovim" },
|
||||
"initializationOptions": {},
|
||||
"capabilities": {
|
||||
"window": {
|
||||
"showMessage": {
|
||||
"messageActionItem": { "additionalPropertiesSupport": false }
|
||||
},
|
||||
"workDoneProgress": true,
|
||||
"showDocument": { "support": true }
|
||||
},
|
||||
"workspace": {
|
||||
"applyEdit": true,
|
||||
"workspaceFolders": true,
|
||||
"semanticTokens": { "refreshSupport": true },
|
||||
"didChangeWatchedFiles": {
|
||||
"dynamicRegistration": true,
|
||||
"relativePatternSupport": true
|
||||
},
|
||||
"workspaceEdit": {
|
||||
"resourceOperations": ["rename", "create", "delete"]
|
||||
},
|
||||
"symbol": {
|
||||
"dynamicRegistration": false,
|
||||
"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
|
||||
]
|
||||
},
|
||||
"hierarchicalWorkspaceSymbolSupport": true
|
||||
},
|
||||
"configuration": true
|
||||
},
|
||||
"textDocument": {
|
||||
"rename": { "prepareSupport": true, "dynamicRegistration": false },
|
||||
"documentHighlight": { "dynamicRegistration": false },
|
||||
"documentSymbol": {
|
||||
"dynamicRegistration": false,
|
||||
"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
|
||||
},
|
||||
"foldingRange": {
|
||||
"lineFoldingOnly": true,
|
||||
"dynamicRegistration": false
|
||||
},
|
||||
"semanticTokens": {
|
||||
"formats": ["relative"],
|
||||
"overlappingTokenSupport": true,
|
||||
"requests": { "range": false, "full": { "delta": true } },
|
||||
"serverCancelSupport": false,
|
||||
"augmentsSyntaxTokens": true,
|
||||
"dynamicRegistration": false,
|
||||
"tokenModifiers": [
|
||||
"declaration",
|
||||
"definition",
|
||||
"readonly",
|
||||
"static",
|
||||
"deprecated",
|
||||
"abstract",
|
||||
"async",
|
||||
"modification",
|
||||
"documentation",
|
||||
"defaultLibrary"
|
||||
],
|
||||
"tokenTypes": [
|
||||
"namespace",
|
||||
"type",
|
||||
"class",
|
||||
"enum",
|
||||
"interface",
|
||||
"struct",
|
||||
"typeParameter",
|
||||
"parameter",
|
||||
"variable",
|
||||
"property",
|
||||
"enumMember",
|
||||
"event",
|
||||
"function",
|
||||
"method",
|
||||
"macro",
|
||||
"keyword",
|
||||
"modifier",
|
||||
"comment",
|
||||
"string",
|
||||
"number",
|
||||
"regexp",
|
||||
"operator",
|
||||
"decorator"
|
||||
],
|
||||
"multilineTokenSupport": false
|
||||
},
|
||||
"publishDiagnostics": {
|
||||
"relatedInformation": true,
|
||||
"tagSupport": { "valueSet": [1, 2] }
|
||||
},
|
||||
"codeAction": {
|
||||
"dataSupport": true,
|
||||
"dynamicRegistration": false,
|
||||
"codeActionLiteralSupport": {
|
||||
"codeActionKind": {
|
||||
"valueSet": [
|
||||
"",
|
||||
"quickfix",
|
||||
"refactor",
|
||||
"refactor.extract",
|
||||
"refactor.inline",
|
||||
"refactor.rewrite",
|
||||
"source",
|
||||
"source.organizeImports"
|
||||
]
|
||||
}
|
||||
},
|
||||
"resolveSupport": { "properties": ["edit"] },
|
||||
"isPreferredSupport": true
|
||||
},
|
||||
"callHierarchy": { "dynamicRegistration": false },
|
||||
"completion": {
|
||||
"completionItem": {
|
||||
"resolveSupport": {
|
||||
"properties": ["documentation", "detail", "additionalTextEdits"]
|
||||
},
|
||||
"snippetSupport": true,
|
||||
"commitCharactersSupport": true,
|
||||
"preselectSupport": true,
|
||||
"deprecatedSupport": true,
|
||||
"documentationFormat": ["markdown", "plaintext"],
|
||||
"tagSupport": { "valueSet": [1] },
|
||||
"insertReplaceSupport": true,
|
||||
"labelDetailsSupport": true
|
||||
},
|
||||
"dynamicRegistration": false,
|
||||
"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
|
||||
]
|
||||
},
|
||||
"contextSupport": false
|
||||
},
|
||||
"declaration": { "linkSupport": true },
|
||||
"definition": { "linkSupport": true },
|
||||
"signatureHelp": {
|
||||
"signatureInformation": {
|
||||
"documentationFormat": ["markdown", "plaintext"],
|
||||
"parameterInformation": { "labelOffsetSupport": true },
|
||||
"activeParameterSupport": true
|
||||
},
|
||||
"dynamicRegistration": false
|
||||
},
|
||||
"synchronization": {
|
||||
"didSave": true,
|
||||
"dynamicRegistration": false,
|
||||
"willSaveWaitUntil": true,
|
||||
"willSave": true
|
||||
},
|
||||
"hover": {
|
||||
"contentFormat": ["markdown", "plaintext"],
|
||||
"dynamicRegistration": false
|
||||
},
|
||||
"typeDefinition": { "linkSupport": true },
|
||||
"references": { "dynamicRegistration": false },
|
||||
"implementation": { "linkSupport": true }
|
||||
}
|
||||
},
|
||||
"trace": "off",
|
||||
"workspaceFolders": []
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue