mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 21:23:45 +00:00
feat: implements experimental/onEnter
(#328)
* feat: implements `experimental/onEnter` * docs: update readme * dev: update snapshot * dev: allow configuration
This commit is contained in:
parent
8d753d8c56
commit
95a68d2559
11 changed files with 373 additions and 3 deletions
|
@ -45,6 +45,10 @@ Language service (LSP) features:
|
|||
- [Workspace Symbols](https://code.visualstudio.com/api/language-extensions/programmatic-language-features#show-all-symbol-definitions-in-folder)
|
||||
- [Code Action](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-code-actions)
|
||||
- Increasing/Decreasing heading levels.
|
||||
- [experimental/onEnter](https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#on-enter)
|
||||
- <kbd>Enter</kbd> inside triple-slash comments automatically inserts `///`
|
||||
- <kbd>Enter</kbd> in the middle or after a trailing space in `//` inserts `//`
|
||||
- <kbd>Enter</kbd> inside `//!` doc comments automatically inserts `//!`
|
||||
|
||||
Extra features:
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ pub(crate) mod signature_help;
|
|||
pub use signature_help::*;
|
||||
pub(crate) mod symbol;
|
||||
pub use symbol::*;
|
||||
pub(crate) mod on_enter;
|
||||
pub use on_enter::*;
|
||||
pub(crate) mod prepare_rename;
|
||||
pub use prepare_rename::*;
|
||||
pub(crate) mod references;
|
||||
|
@ -222,6 +224,8 @@ mod polymorphic {
|
|||
SelectionRange(SelectionRangeRequest),
|
||||
InteractCodeContext(InteractCodeContextRequest),
|
||||
|
||||
OnEnter(OnEnterRequest),
|
||||
|
||||
DocumentMetrics(DocumentMetricsRequest),
|
||||
ServerInfo(ServerInfoRequest),
|
||||
}
|
||||
|
@ -255,6 +259,8 @@ mod polymorphic {
|
|||
CompilerQueryRequest::SelectionRange(..) => ContextFreeUnique,
|
||||
CompilerQueryRequest::InteractCodeContext(..) => PinnedFirst,
|
||||
|
||||
CompilerQueryRequest::OnEnter(..) => ContextFreeUnique,
|
||||
|
||||
CompilerQueryRequest::DocumentMetrics(..) => PinnedFirst,
|
||||
CompilerQueryRequest::ServerInfo(..) => Mergeable,
|
||||
}
|
||||
|
@ -286,6 +292,7 @@ mod polymorphic {
|
|||
CompilerQueryRequest::FoldingRange(req) => &req.path,
|
||||
CompilerQueryRequest::SelectionRange(req) => &req.path,
|
||||
CompilerQueryRequest::InteractCodeContext(req) => &req.path,
|
||||
CompilerQueryRequest::OnEnter(req) => &req.path,
|
||||
|
||||
CompilerQueryRequest::DocumentMetrics(req) => &req.path,
|
||||
CompilerQueryRequest::ServerInfo(..) => return None,
|
||||
|
@ -320,6 +327,8 @@ mod polymorphic {
|
|||
SelectionRange(Option<Vec<SelectionRange>>),
|
||||
InteractCodeContext(Option<Vec<InteractCodeContextResponse>>),
|
||||
|
||||
OnEnter(Option<Vec<TextEdit>>),
|
||||
|
||||
DocumentMetrics(Option<DocumentMetricsResponse>),
|
||||
ServerInfo(Option<HashMap<String, ServerInfoResponse>>),
|
||||
}
|
||||
|
|
98
crates/tinymist-query/src/on_enter.rs
Normal file
98
crates/tinymist-query/src/on_enter.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
//! <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#on-enter>
|
||||
|
||||
use crate::{prelude::*, SyntaxRequest};
|
||||
|
||||
/// The [`experimental/onEnter`] request is sent from client to server to handle
|
||||
/// the <kbd>Enter</kbd> key press.
|
||||
///
|
||||
/// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
|
||||
/// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
|
||||
/// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!`
|
||||
///
|
||||
/// [`experimental/onEnter`]: https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#on-enter
|
||||
///
|
||||
/// # Compatibility
|
||||
///
|
||||
/// This request was introduced in specification version 3.10.0.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OnEnterRequest {
|
||||
/// The path of the document to get folding ranges for.
|
||||
pub path: PathBuf,
|
||||
/// The source code position to request for.
|
||||
pub position: LspPosition,
|
||||
}
|
||||
|
||||
impl SyntaxRequest for OnEnterRequest {
|
||||
type Response = Vec<TextEdit>;
|
||||
|
||||
fn request(
|
||||
self,
|
||||
source: &Source,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> Option<Self::Response> {
|
||||
let root = LinkedNode::new(source.root());
|
||||
let cursor = lsp_to_typst::position(self.position, position_encoding, source)?;
|
||||
let leaf = root.leaf_at(cursor)?;
|
||||
|
||||
let worker = OnEnterWorker {
|
||||
source,
|
||||
position_encoding,
|
||||
};
|
||||
|
||||
if matches!(leaf.kind(), SyntaxKind::LineComment) {
|
||||
return worker.enter_line_doc_comment(&leaf, cursor);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct OnEnterWorker<'a> {
|
||||
source: &'a Source,
|
||||
position_encoding: PositionEncoding,
|
||||
}
|
||||
|
||||
impl OnEnterWorker<'_> {
|
||||
fn enter_line_doc_comment(&self, leaf: &LinkedNode, cursor: usize) -> Option<Vec<TextEdit>> {
|
||||
let skipper = |n: &LinkedNode| {
|
||||
matches!(
|
||||
n.kind(),
|
||||
SyntaxKind::Space | SyntaxKind::Linebreak | SyntaxKind::LineComment
|
||||
)
|
||||
};
|
||||
let parent = leaf.parent()?;
|
||||
let till_curr = parent.children().take(leaf.index());
|
||||
let first_index = till_curr.rev().take_while(skipper).count();
|
||||
let comment_group_cnt = parent
|
||||
.children()
|
||||
.skip(leaf.index().saturating_sub(first_index))
|
||||
.take_while(skipper)
|
||||
.filter(|e| matches!(e.kind(), SyntaxKind::LineComment))
|
||||
.count();
|
||||
|
||||
let comment_prefix = {
|
||||
let mut p = unscanny::Scanner::new(leaf.text());
|
||||
p.eat_while('/');
|
||||
p.eat_if('!');
|
||||
p.before()
|
||||
};
|
||||
|
||||
// Continuing single-line non-doc comments (like this one :) ) is annoying
|
||||
if comment_group_cnt <= 1 && comment_prefix == "//" {
|
||||
return None;
|
||||
}
|
||||
|
||||
// todo: indent
|
||||
let indent = "";
|
||||
// todo: remove_trailing_whitespace
|
||||
|
||||
let rng = cursor..cursor;
|
||||
|
||||
let edit = TextEdit {
|
||||
range: typst_to_lsp::range(rng, self.source, self.position_encoding),
|
||||
new_text: format!("\n{indent}{comment_prefix} $0"),
|
||||
};
|
||||
|
||||
Some(vec![edit])
|
||||
}
|
||||
}
|
|
@ -277,6 +277,7 @@ impl TypstLanguageServer {
|
|||
request_fn!(GotoDeclaration, Self::goto_declaration),
|
||||
request_fn!(References, Self::references),
|
||||
request_fn!(WorkspaceSymbolRequest, Self::symbol),
|
||||
request_fn!(OnEnter, Self::on_enter),
|
||||
request_fn_!(ExecuteCommand, Self::on_execute_command),
|
||||
])
|
||||
}
|
||||
|
@ -1182,6 +1183,11 @@ impl TypstLanguageServer {
|
|||
let pattern = (!params.query.is_empty()).then_some(params.query);
|
||||
run_query!(self.Symbol(pattern))
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, params: TextDocumentPositionParams) -> LspResult<Option<Vec<TextEdit>>> {
|
||||
let (path, position) = as_path_pos(params);
|
||||
run_query!(self.OnEnter(path, position))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@ -1255,6 +1261,13 @@ pub(crate) fn result_to_response<T: Serialize>(
|
|||
}
|
||||
}
|
||||
|
||||
struct OnEnter;
|
||||
impl lsp_types::request::Request for OnEnter {
|
||||
type Params = TextDocumentPositionParams;
|
||||
type Result = Option<Vec<TextEdit>>;
|
||||
const METHOD: &'static str = "experimental/onEnter";
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_path() {
|
||||
let uri = Url::parse("untitled:/path/to/file").unwrap();
|
||||
|
|
|
@ -5,7 +5,7 @@ use itertools::Itertools;
|
|||
use log::info;
|
||||
use lsp_types::*;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{Map, Value as JsonValue};
|
||||
use serde_json::{json, Map, Value as JsonValue};
|
||||
use tinymist_query::{get_semantic_tokens_options, PositionEncoding};
|
||||
use tokio::sync::mpsc;
|
||||
use typst_ts_core::ImmutPath;
|
||||
|
@ -397,6 +397,10 @@ impl Init {
|
|||
code_lens_provider: Some(CodeLensOptions {
|
||||
resolve_provider: Some(false),
|
||||
}),
|
||||
|
||||
experimental: Some(json!({
|
||||
"onEnter": true,
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
|
|
|
@ -262,6 +262,7 @@ impl TypstLanguageServer {
|
|||
FoldingRange(req) => query_source!(self, FoldingRange, req),
|
||||
SelectionRange(req) => query_source!(self, SelectionRange, req),
|
||||
DocumentSymbol(req) => query_source!(self, DocumentSymbol, req),
|
||||
OnEnter(req) => query_source!(self, OnEnter, req),
|
||||
ColorPresentation(req) => Ok(CompilerQueryResponse::ColorPresentation(req.request())),
|
||||
_ => {
|
||||
let client = &mut self.primary;
|
||||
|
|
|
@ -96,6 +96,12 @@
|
|||
"Do not use semantic tokens for syntax highlighting"
|
||||
]
|
||||
},
|
||||
"tinymist.onEnterEvent": {
|
||||
"title": "Handling on enter events",
|
||||
"description": "Enable or disable [experimental/onEnter](https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#on-enter) (LSP onEnter feature) to allow automatic insertion of characters on enter, such as `///` for comments. Note: restarting the editor is required to change this setting.",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"tinymist.systemFonts": {
|
||||
"title": "Whether to load system fonts for Typst compiler",
|
||||
"description": "A flag that determines whether to load system fonts for Typst compiler, which is useful for ensuring reproducible compilation. If set to null or not set, the extension will use the default behavior of the Typst compiler. Note: You need to restart LSP to change this options. ",
|
||||
|
@ -464,6 +470,13 @@
|
|||
"category": "Typst"
|
||||
}
|
||||
],
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "tinymist.onEnter",
|
||||
"key": "enter",
|
||||
"when": "editorTextFocus && !editorReadonly && editorLangId == typst && !suggestWidgetVisible && !editorHasMultipleSelections && vim.mode != 'Normal' && vim.mode != 'Visual' && vim.mode != 'VisualBlock' && vim.mode != 'VisualLine' && vim.mode != 'SearchInProgressMode' && vim.mode != 'CommandlineInProgress' && vim.mode != 'Replace' && vim.mode != 'EasyMotionMode' && vim.mode != 'EasyMotionInputMode' && vim.mode != 'SurroundInputMode'"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
import * as vscode from "vscode";
|
||||
import * as path from "path";
|
||||
import * as child_process from "child_process";
|
||||
import * as lc from "vscode-languageclient";
|
||||
|
||||
import {
|
||||
LanguageClient,
|
||||
|
@ -24,6 +25,7 @@ import {
|
|||
getUserPackageData,
|
||||
} from "./editor-tools";
|
||||
import { triggerStatusBar, wordCountItemProcess } from "./ui-extends";
|
||||
import { applySnippetTextEdits } from "./snippets";
|
||||
|
||||
let client: LanguageClient | undefined = undefined;
|
||||
|
||||
|
@ -34,11 +36,15 @@ export function activate(context: ExtensionContext): Promise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
let enableOnEnter = false;
|
||||
|
||||
async function startClient(context: ExtensionContext): Promise<void> {
|
||||
let config: Record<string, any> = JSON.parse(
|
||||
JSON.stringify(workspace.getConfiguration("tinymist"))
|
||||
);
|
||||
|
||||
enableOnEnter = !!config.onEnterEvent;
|
||||
|
||||
{
|
||||
const keys = Object.keys(config);
|
||||
let values = keys.map((key) => config[key]);
|
||||
|
@ -130,6 +136,8 @@ async function startClient(context: ExtensionContext): Promise<void> {
|
|||
});
|
||||
|
||||
context.subscriptions.push(
|
||||
commands.registerCommand("tinymist.onEnter", onEnterHandler()),
|
||||
|
||||
commands.registerCommand("tinymist.exportCurrentPdf", () => commandExport("Pdf")),
|
||||
commands.registerCommand("tinymist.getCurrentDocumentMetrics", () =>
|
||||
commandGetCurrentDocumentMetrics()
|
||||
|
@ -246,6 +254,55 @@ function validateServer(path: string): { valid: true } | { valid: false; message
|
|||
}
|
||||
}
|
||||
|
||||
function activeTypstEditor() {
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor || editor.document.languageId !== "typst") {
|
||||
return;
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
export const onEnter = new lc.RequestType<lc.TextDocumentPositionParams, lc.TextEdit[], void>(
|
||||
"experimental/onEnter"
|
||||
);
|
||||
|
||||
export function onEnterHandler() {
|
||||
async function handleKeypress() {
|
||||
if (!enableOnEnter) return false;
|
||||
|
||||
const editor = activeTypstEditor();
|
||||
|
||||
if (!editor || !client) return false;
|
||||
|
||||
const lcEdits = await client
|
||||
.sendRequest(onEnter, {
|
||||
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||
editor.document
|
||||
),
|
||||
position: client.code2ProtocolConverter.asPosition(editor.selection.active),
|
||||
})
|
||||
.catch((_error: any) => {
|
||||
// client.handleFailedRequest(OnEnterRequest.type, error, null);
|
||||
return null;
|
||||
});
|
||||
if (!lcEdits) return false;
|
||||
|
||||
const edits = await client.protocol2CodeConverter.asTextEdits(lcEdits);
|
||||
await applySnippetTextEdits(editor, edits);
|
||||
return true;
|
||||
}
|
||||
|
||||
return async () => {
|
||||
try {
|
||||
if (await handleKeypress()) return;
|
||||
} catch (e) {
|
||||
console.error("onEnter failed", e);
|
||||
}
|
||||
|
||||
await vscode.commands.executeCommand("default:type", { text: "\n" });
|
||||
};
|
||||
}
|
||||
|
||||
async function commandExport(mode: string, extraOpts?: any): Promise<string | undefined> {
|
||||
const activeEditor = window.activeTextEditor;
|
||||
if (activeEditor === undefined) {
|
||||
|
|
152
editors/vscode/src/snippets.ts
Normal file
152
editors/vscode/src/snippets.ts
Normal file
|
@ -0,0 +1,152 @@
|
|||
// https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/editors/code/src/snippets.ts
|
||||
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import { strict as nativeAssert } from "assert";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
export function assert(condition: boolean, explanation: string): asserts condition {
|
||||
try {
|
||||
nativeAssert(condition, explanation);
|
||||
} catch (err) {
|
||||
console.error(`Assertion failed:`, explanation);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export type SnippetTextDocumentEdit = [vscode.Uri, (vscode.TextEdit | vscode.SnippetTextEdit)[]];
|
||||
|
||||
export async function applySnippetWorkspaceEdit(
|
||||
edit: vscode.WorkspaceEdit,
|
||||
editEntries: SnippetTextDocumentEdit[]
|
||||
) {
|
||||
if (editEntries.length === 1) {
|
||||
const [uri, edits] = unwrapUndefinable(editEntries[0]);
|
||||
const editor = await editorFromUri(uri);
|
||||
if (editor) {
|
||||
edit.set(uri, removeLeadingWhitespace(editor, edits));
|
||||
await vscode.workspace.applyEdit(edit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (const [uri, edits] of editEntries) {
|
||||
const editor = await editorFromUri(uri);
|
||||
if (editor) {
|
||||
await editor.edit((builder) => {
|
||||
for (const indel of edits) {
|
||||
assert(
|
||||
!(indel instanceof vscode.SnippetTextEdit),
|
||||
`bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`
|
||||
);
|
||||
builder.replace(indel.range, indel.newText);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undefined> {
|
||||
if (vscode.window.activeTextEditor?.document.uri !== uri) {
|
||||
// `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed
|
||||
await vscode.window.showTextDocument(uri, {});
|
||||
}
|
||||
return vscode.window.visibleTextEditors.find(
|
||||
(it) => it.document.uri.toString() === uri.toString()
|
||||
);
|
||||
}
|
||||
|
||||
export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
const snippetEdits = toSnippetTextEdits(edits);
|
||||
edit.set(editor.document.uri, removeLeadingWhitespace(editor, snippetEdits));
|
||||
await vscode.workspace.applyEdit(edit);
|
||||
}
|
||||
|
||||
function hasSnippet(snip: string): boolean {
|
||||
const m = snip.match(/\$\d+|\{\d+:[^}]*\}/);
|
||||
return m != null;
|
||||
}
|
||||
|
||||
function toSnippetTextEdits(
|
||||
edits: vscode.TextEdit[]
|
||||
): (vscode.TextEdit | vscode.SnippetTextEdit)[] {
|
||||
return edits.map((textEdit) => {
|
||||
// Note: text edits without any snippets are returned as-is instead of
|
||||
// being wrapped in a SnippetTextEdit, as otherwise it would be
|
||||
// treated as if it had a tab stop at the end.
|
||||
if (hasSnippet(textEdit.newText)) {
|
||||
return new vscode.SnippetTextEdit(
|
||||
textEdit.range,
|
||||
new vscode.SnippetString(textEdit.newText)
|
||||
);
|
||||
} else {
|
||||
return textEdit;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the leading whitespace from snippet edits, so as to not double up
|
||||
* on indentation.
|
||||
*
|
||||
* Snippet edits by default adjust any multi-line snippets to match the
|
||||
* indentation of the line to insert at. Unfortunately, we (the server) also
|
||||
* include the required indentation to match what we line insert at, so we end
|
||||
* up doubling up the indentation. Since there isn't any way to tell vscode to
|
||||
* not fixup indentation for us, we instead opt to remove the indentation and
|
||||
* then let vscode add it back in.
|
||||
*
|
||||
* This assumes that the source snippet text edits have the required
|
||||
* indentation, but that's okay as even without this workaround and the problem
|
||||
* to workaround, those snippet edits would already be inserting at the wrong
|
||||
* indentation.
|
||||
*/
|
||||
function removeLeadingWhitespace(
|
||||
editor: vscode.TextEditor,
|
||||
edits: (vscode.TextEdit | vscode.SnippetTextEdit)[]
|
||||
) {
|
||||
return edits.map((edit) => {
|
||||
if (edit instanceof vscode.SnippetTextEdit) {
|
||||
const snippetEdit: vscode.SnippetTextEdit = edit;
|
||||
const firstLineEnd = snippetEdit.snippet.value.indexOf("\n");
|
||||
|
||||
if (firstLineEnd !== -1) {
|
||||
// Is a multi-line snippet, remove the indentation which
|
||||
// would be added back in by vscode.
|
||||
const startLine = editor.document.lineAt(snippetEdit.range.start.line);
|
||||
const leadingWhitespace = getLeadingWhitespace(
|
||||
startLine.text,
|
||||
0,
|
||||
startLine.firstNonWhitespaceCharacterIndex
|
||||
);
|
||||
|
||||
const [firstLine, rest] = splitAt(snippetEdit.snippet.value, firstLineEnd + 1);
|
||||
const unindentedLines = rest
|
||||
.split("\n")
|
||||
.map((line) => line.replace(leadingWhitespace, ""))
|
||||
.join("\n");
|
||||
|
||||
snippetEdit.snippet.value = firstLine + unindentedLines;
|
||||
}
|
||||
|
||||
return snippetEdit;
|
||||
} else {
|
||||
return edit;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// based on https://github.com/microsoft/vscode/blob/main/src/vs/base/common/strings.ts#L284
|
||||
function getLeadingWhitespace(str: string, start: number = 0, end: number = str.length): string {
|
||||
for (let i = start; i < end; i++) {
|
||||
const chCode = str.charCodeAt(i);
|
||||
if (chCode !== " ".charCodeAt(0) && chCode !== " ".charCodeAt(0)) {
|
||||
return str.substring(start, i);
|
||||
}
|
||||
}
|
||||
return str.substring(start, end);
|
||||
}
|
||||
|
||||
function splitAt(str: string, index: number): [string, string] {
|
||||
return [str.substring(0, index), str.substring(index)];
|
||||
}
|
19
editors/vscode/src/undefinable.ts
Normal file
19
editors/vscode/src/undefinable.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export type NotUndefined<T> = T extends undefined ? never : T;
|
||||
|
||||
export type Undefinable<T> = T | undefined;
|
||||
|
||||
function isNotUndefined<T>(input: Undefinable<T>): input is NotUndefined<T> {
|
||||
return input !== undefined;
|
||||
}
|
||||
|
||||
export function expectNotUndefined<T>(input: Undefinable<T>, msg: string): NotUndefined<T> {
|
||||
if (isNotUndefined(input)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
throw new TypeError(msg);
|
||||
}
|
||||
|
||||
export function unwrapUndefinable<T>(input: Undefinable<T>): NotUndefined<T> {
|
||||
return expectNotUndefined(input, `unwrapping \`undefined\``);
|
||||
}
|
|
@ -374,7 +374,7 @@ fn e2e() {
|
|||
});
|
||||
|
||||
let hash = replay_log(&tinymist_binary, &root.join("neovim"));
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:3d34c7ce3c351dda8198c2c78f7c643a");
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:ceba0fb43113a519f0caf8debde61357");
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -385,7 +385,7 @@ fn e2e() {
|
|||
});
|
||||
|
||||
let hash = replay_log(&tinymist_binary, &root.join("vscode"));
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:74ed8df5e8b474e3565659a09a1d7f38");
|
||||
insta::assert_snapshot!(hash, @"siphash128_13:8400b228493a1b73747f2bb0cb93f4bd");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue