mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-12-23 08:47:50 +00:00
Merge c2df77dd5e into 0bfe655ada
This commit is contained in:
commit
ecbfeb59e5
5 changed files with 164 additions and 291 deletions
|
|
@ -112,7 +112,20 @@ impl SemanticRequest for InteractCodeContextRequest {
|
|||
for query in self.query {
|
||||
responses.push(query.and_then(|query| match query {
|
||||
InteractCodeContextQuery::PathAt { code, inputs: base } => {
|
||||
let res = eval_path_expr(ctx, &code, base)?;
|
||||
let res = eval_path_expr(ctx, &code, base)
|
||||
.and_then(|res| {
|
||||
QueryResult::success(res.map(|mut res| {
|
||||
res.edits = resolve_edits(ctx, res.edits);
|
||||
res
|
||||
}))
|
||||
})
|
||||
.and_then(|res| match serde_json::to_value(res) {
|
||||
Ok(value) => QueryResult::success(value),
|
||||
Err(e) => QueryResult::error(eco_format!(
|
||||
"failed to serialize script result: {e}"
|
||||
)),
|
||||
});
|
||||
// serde_json::Value
|
||||
Some(InteractCodeContextResponse::PathAt(res))
|
||||
}
|
||||
InteractCodeContextQuery::ModeAt { position } => {
|
||||
|
|
@ -217,16 +230,29 @@ impl InteractCodeContextRequest {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct PathAtOutput {
|
||||
dir: PathBuf,
|
||||
edits: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl PathAtOutput {
|
||||
fn dir(dir: PathBuf) -> Self {
|
||||
Self { dir, edits: None }
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_path_expr(
|
||||
ctx: &mut LocalContext,
|
||||
code: &str,
|
||||
inputs: Dict,
|
||||
) -> Option<QueryResult<serde_json::Value>> {
|
||||
) -> QueryResult<Option<PathAtOutput>> {
|
||||
let entry = ctx.world().entry_state();
|
||||
let path = if code.starts_with("{") && code.ends_with("}") {
|
||||
let id = entry
|
||||
.select_in_workspace(Path::new("/__path__.typ"))
|
||||
.main()?;
|
||||
let id = match entry.select_in_workspace(Path::new("/__path__.typ")).main() {
|
||||
Some(id) => id,
|
||||
None => return QueryResult::error("main file not found".into()),
|
||||
};
|
||||
|
||||
let inputs = make_sys(&entry, ctx.world().inputs(), inputs);
|
||||
let (inputs, root, dir, name) = match inputs {
|
||||
|
|
@ -262,17 +288,28 @@ fn eval_path_expr(
|
|||
let expr = match expr.cast::<ast::Code>() {
|
||||
Some(v) => v,
|
||||
None => bail!(
|
||||
"code is not a valid code expression: kind={:?}",
|
||||
"script is not a valid code expression: kind={:?}",
|
||||
expr.kind()
|
||||
),
|
||||
};
|
||||
match expr.eval(vm) {
|
||||
Ok(value) => serde_json::to_value(value).context_ut("failed to serialize path"),
|
||||
Ok(value) => match value {
|
||||
Value::None => Ok(None),
|
||||
Value::Str(s) => Ok(Some(PathAtOutput::dir(PathBuf::from(s.as_str())))),
|
||||
value @ Value::Dict(..) => {
|
||||
let s = serde_json::to_value(value)
|
||||
.context_ut("failed to serialize script result")?;
|
||||
serde_json::from_value::<PathAtOutput>(s)
|
||||
.context_ut("failed to deserialize script result")
|
||||
.map(Some)
|
||||
}
|
||||
_ => bail!("script result is not a string or dictionary: {value:?}"),
|
||||
},
|
||||
Err(e) => {
|
||||
let res =
|
||||
print_diagnostics_to_string(&world, e.iter(), DiagnosticFormat::Human);
|
||||
let err = res.unwrap_or_else(|e| e);
|
||||
bail!("failed to evaluate path expression: {err}")
|
||||
bail!("failed to evaluate script: {err}")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -280,11 +317,17 @@ fn eval_path_expr(
|
|||
PathPattern::new(code)
|
||||
.substitute(&entry)
|
||||
.context_ut("failed to substitute path pattern")
|
||||
.and_then(|path| {
|
||||
serde_json::to_value(path.deref()).context_ut("failed to serialize path")
|
||||
})
|
||||
.map(|dir| Some(PathAtOutput::dir(dir.as_ref().to_owned())))
|
||||
};
|
||||
Some(path.into())
|
||||
path.into()
|
||||
}
|
||||
|
||||
// todo: implement this
|
||||
fn resolve_edits(
|
||||
_ctx: &mut LocalContext,
|
||||
edits: Option<serde_json::Value>,
|
||||
) -> Option<serde_json::Value> {
|
||||
edits
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
|
|
@ -392,6 +435,17 @@ impl<T> QueryResult<T> {
|
|||
pub fn error(error: EcoString) -> Self {
|
||||
Self::Error { error }
|
||||
}
|
||||
|
||||
/// Applies a function to the value of a successful result.
|
||||
pub fn and_then<U, F>(self, f: F) -> QueryResult<U>
|
||||
where
|
||||
F: FnOnce(T) -> QueryResult<U>,
|
||||
{
|
||||
match self {
|
||||
QueryResult::Success { value } => f(value),
|
||||
QueryResult::Error { error } => QueryResult::error(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E: std::error::Error> From<Result<T, E>> for QueryResult<T> {
|
||||
|
|
@ -411,7 +465,7 @@ mod tests {
|
|||
use crate::tests::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
fn path() {
|
||||
snapshot_testing("code_context_path_at", &|ctx, path| {
|
||||
let patterns = [
|
||||
"$root/$dir/$name",
|
||||
|
|
@ -460,4 +514,83 @@ mod tests {
|
|||
assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snippet() {
|
||||
// type ModeEdit = string | {
|
||||
// kind: "by-mode";
|
||||
// math?: string;
|
||||
// markup?: string;
|
||||
// code?: string;
|
||||
// comment?: string;
|
||||
// string?: string;
|
||||
// raw?: string;
|
||||
// rest?: string;
|
||||
// };
|
||||
|
||||
// export async function createModeEdit(edit: ModeEdit) {
|
||||
// const {
|
||||
// kind,
|
||||
// math,
|
||||
// comment,
|
||||
// markup,
|
||||
// code,
|
||||
// string: stringContent,
|
||||
// raw,
|
||||
// rest,
|
||||
// }: Record<string, string> = edit.newText;
|
||||
// const newText = kind === "by-mode" ? rest || "" : "";
|
||||
|
||||
// const res = await vscode.commands.executeCommand<
|
||||
// [{ mode: "math" | "markup" | "code" | "comment" | "string" |
|
||||
// "raw" }] >("tinymist.interactCodeContext", {
|
||||
// textDocument: {
|
||||
// uri: activeDocument.uri.toString(),
|
||||
// },
|
||||
// query: [
|
||||
// {
|
||||
// kind: "modeAt",
|
||||
// position: {
|
||||
// line: selectionStart.line,
|
||||
// character: selectionStart.character,
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
|
||||
// const mode = res[0].mode;
|
||||
|
||||
// await editor.edit((editBuilder) => {
|
||||
// if (mode === "math") {
|
||||
// // todo: whether to keep stupid
|
||||
// // if it is before an identifier character, then add a space
|
||||
// let replaceText = math || newText;
|
||||
// const range = new vscode.Range(
|
||||
// selectionStart.with(undefined, selectionStart.character - 1),
|
||||
// selectionStart,
|
||||
// );
|
||||
// const before = selectionStart.character > 0 ?
|
||||
// activeDocument.getText(range) : ""; if (before.match(/
|
||||
// [\p{XID_Start}\p{XID_Continue}_]/u)) { replaceText =
|
||||
// ` ${math}`; }
|
||||
|
||||
// editBuilder.replace(selection, replaceText);
|
||||
// } else if (mode === "markup") {
|
||||
// editBuilder.replace(selection, markup || newText);
|
||||
// } else if (mode === "comment") {
|
||||
// editBuilder.replace(selection, comment || markup || newText);
|
||||
// } else if (mode === "string") {
|
||||
// editBuilder.replace(selection, stringContent || raw ||
|
||||
// newText); } else if (mode === "raw") {
|
||||
// editBuilder.replace(selection, raw || stringContent ||
|
||||
// newText); } else if (mode === "code") {
|
||||
// editBuilder.replace(selection, code || newText);
|
||||
// } else {
|
||||
// editBuilder.replace(selection, newText);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/code_context.rs
|
||||
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
|
||||
input_file: crates/tinymist-query/src/fixtures/code_context_path_at/at_root.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"code": "$root/$dir/$name",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "x_at_root"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "$root/$name",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "x_at_root"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "$root/assets",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "assets"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "$root/assets/$name",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "assets/x_at_root"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ join(root, \"x\", dir, \"y\", name) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "x/y/x_at_root"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ join(root, 1) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"error": "crates/tinymist-query/src/code_context.rs:275:21: failed to evaluate path expression: error: join argument is not a string: 1\n ┌─ /__redacted_path__.typ:1:0\n │\n1 │ { join(root, 1) }\n │ ^^^^^^^^^^^^^^^^^\n\n",
|
||||
"kind": "pathAt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ join(roo, 1) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"error": "crates/tinymist-query/src/code_context.rs:275:21: failed to evaluate path expression: error: unknown variable: roo\n ┌─ /__redacted_path__.typ:1:0\n │\n1 │ { join(roo, 1) }\n │ ^^^^^^^^^^^^^^^^\n\n",
|
||||
"kind": "pathAt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ import \"/resolve.typ\": resolve; resolve(join, root, dir, name) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": {
|
||||
"file": "images/img.png",
|
||||
"on-conflict": "import \"/resolve.typ\": on-conflict; on-conflict(join, root, dir, name)"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ import \"/resolve.typ\": resolve; resolve(join, root, dir, name) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "text.md",
|
||||
"x-path-input-uri": "https://huh.io/text.md"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "assets/x_at_root"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/code_context.rs
|
||||
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
|
||||
input_file: crates/tinymist-query/src/fixtures/code_context_path_at/in_dir.typ
|
||||
---
|
||||
[
|
||||
{
|
||||
"code": "$root/$dir/$name",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "the_dir/x_in_dir"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "$root/$name",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "x_in_dir"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "$root/assets",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "assets"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "$root/assets/$name",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "assets/x_in_dir"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ join(root, \"x\", dir, \"y\", name) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"kind": "pathAt",
|
||||
"value": "x/the_dir/y/x_in_dir"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ join(root, 1) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"error": "crates/tinymist-query/src/code_context.rs:275:21: failed to evaluate path expression: error: join argument is not a string: 1\n ┌─ /__redacted_path__.typ:1:0\n │\n1 │ { join(root, 1) }\n │ ^^^^^^^^^^^^^^^^^\n\n",
|
||||
"kind": "pathAt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ join(roo, 1) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"error": "crates/tinymist-query/src/code_context.rs:275:21: failed to evaluate path expression: error: unknown variable: roo\n ┌─ /__redacted_path__.typ:1:0\n │\n1 │ { join(roo, 1) }\n │ ^^^^^^^^^^^^^^^^\n\n",
|
||||
"kind": "pathAt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ import \"/resolve.typ\": resolve; resolve(join, root, dir, name) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "img.png",
|
||||
"x-path-input-uri": "https://huh.io/img.png"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"error": "crates/tinymist-query/src/code_context.rs:275:21: failed to evaluate path expression: error: file not found (searched at /__redacted_path__.typ)\n ┌─ /__redacted_path__.typ:1:0\n │\n1 │ { import \"/resolve.typ\": resolve; resolve(join, root, dir, name) }\n │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n",
|
||||
"kind": "pathAt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"code": "{ import \"/resolve.typ\": resolve; resolve(join, root, dir, name) }",
|
||||
"inputs": {
|
||||
"x-path-context": "vscode-paste",
|
||||
"x-path-input-name": "text.md",
|
||||
"x-path-input-uri": "https://huh.io/text.md"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"error": "crates/tinymist-query/src/code_context.rs:275:21: failed to evaluate path expression: error: file not found (searched at /__redacted_path__.typ)\n ┌─ /__redacted_path__.typ:1:0\n │\n1 │ { import \"/resolve.typ\": resolve; resolve(join, root, dir, name) }\n │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n",
|
||||
"kind": "pathAt"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -681,14 +681,23 @@ export async function getDesiredNewFilePath(
|
|||
if ("error" in pathAt) {
|
||||
throw new Error(pathAt.error);
|
||||
}
|
||||
const path = pathAt.value;
|
||||
if (typeof path !== "string") {
|
||||
throw new Error(
|
||||
`expect paste script to return an object { value: string }, got { value: ${typeof path} }`,
|
||||
);
|
||||
let dir = pathAt.value;
|
||||
if (typeof dir !== "string") {
|
||||
if (typeof dir !== 'object') {
|
||||
throw new Error(
|
||||
`expect paste script to return an object { value: string | object }, got { value: ${JSON.stringify(dir)} }`,
|
||||
);
|
||||
} else {
|
||||
if (!("dir" in dir)) {
|
||||
throw new Error(
|
||||
`expect paste script to return an object { value: { dir: string } }, got { value: ${JSON.stringify(dir)} }`,
|
||||
);
|
||||
}
|
||||
dir = dir.dir;
|
||||
}
|
||||
}
|
||||
|
||||
const newFileDir = vscode.Uri.file(path);
|
||||
const newFileDir = vscode.Uri.file(dir);
|
||||
const workspaceFolder = vscode.workspace.getWorkspaceFolder(newFileDir);
|
||||
if (!workspaceFolder) {
|
||||
throw new Error(
|
||||
|
|
@ -768,7 +777,7 @@ export class UriList {
|
|||
|
||||
constructor(
|
||||
public readonly entries: ReadonlyArray<{ readonly uri: vscode.Uri; readonly str: string }>,
|
||||
) {}
|
||||
) { }
|
||||
}
|
||||
|
||||
const externalUriSchemes: ReadonlySet<string> = new Set([
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ async function main() {
|
|||
// Run tests using the minimal supported version and the latest one.
|
||||
for (const version of [minimalVersion, "stable"]) {
|
||||
for (const uri of [
|
||||
// path.resolve(extensionDevelopmentPath, "e2e-workspaces/export"),
|
||||
path.resolve(extensionDevelopmentPath, "e2e-workspaces/book"),
|
||||
path.resolve(extensionDevelopmentPath, "e2e-workspaces/export"),
|
||||
// path.resolve(extensionDevelopmentPath, "e2e-workspaces/book"),
|
||||
// undefined,
|
||||
]) {
|
||||
await runTests({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue