feat: bootstrap lsp-free features in web (#1105)

* feat: bootstrap lsp-free features in web

* ci: update build script

* ci: update system build script

* dev: touch extension file in web

* dev: touch extension file in system

* fix: bug import

* fix: bug touch
This commit is contained in:
Myriad-Dreamin 2025-01-03 13:47:52 +08:00 committed by GitHub
parent d32f6261f1
commit d7dd2f30cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 459 additions and 195 deletions

View file

@ -409,7 +409,7 @@ jobs:
- name: Build tinymist vscode extension
run: |
yarn
yarn run compile
yarn run compile:web
working-directory: ./editors/vscode
- name: Build tinymist library
run: yarn build

2
.vscode/launch.json vendored
View file

@ -26,7 +26,7 @@
"outFiles": [
"${workspaceFolder}/editors/vscode/out/**/*.js"
],
"preLaunchTask": "VS Code Extension Prelaunch"
"preLaunchTask": "VS Code Extension Prelaunch [Web]"
},
{
"name": "Run Extension [Release]",

14
.vscode/tasks.json vendored
View file

@ -12,6 +12,13 @@
],
"dependsOrder": "sequence",
},
{
"label": "VS Code Extension Prelaunch [Web]",
"dependsOn": [
"Compile VS Code Extension [Web]",
],
"dependsOrder": "sequence",
},
{
"label": "VS Code Extension Prelaunch [Release]",
"dependsOn": [
@ -41,6 +48,13 @@
"path": "editors/vscode",
"group": "build",
},
{
"label": "Compile VS Code Extension [Web]",
"type": "npm",
"script": "compile:web",
"path": "editors/vscode",
"group": "build",
},
{
"label": "Generate VS Code Extension Bundle",
"type": "npm",

View file

@ -0,0 +1,16 @@
import { build } from "esbuild";
import * as fs from 'fs';
if (!fs.existsSync('./out/extension.web.js')) {
fs.mkdirSync('./out', { recursive: true });
fs.writeFileSync('./out/extension.web.js', '');
}
build({
entryPoints: ["./src/extension.ts"],
bundle: true,
outfile: "./out/extension.js",
external: ["vscode"],
format: "cjs",
platform: "node",
}).catch(() => process.exit(1));

View file

@ -0,0 +1,29 @@
import { build } from "esbuild";
import { polyfillNode } from "esbuild-plugin-polyfill-node";
import * as fs from 'fs';
if (!fs.existsSync('./out/extension.js')) {
fs.mkdirSync('./out', { recursive: true });
fs.writeFileSync('./out/extension.js', '');
}
build({
entryPoints: ["./src/extension.web.ts"],
bundle: true,
outfile: "./out/extension.web.js",
external: ["vscode"],
format: "cjs",
target: ["es2020", "chrome61", "edge18", "firefox60"],
// Node.js global to browser globalThis
define: {
global: 'globalThis'
},
plugins: [
polyfillNode({
polyfills: {
crypto: "empty",
},
// Options (optional)
}),
],
}).catch(() => process.exit(1));

View file

@ -1095,12 +1095,14 @@
"scripts": {
"build:frontend": "cd ../../ && yarn build:preview && yarn build:editor-tools",
"build:syntax": "cd ../../syntaxes/textmate && yarn run compile && yarn run bundle",
"build-web-base": "esbuild ./src/extension.web.ts --bundle --outfile=out/extension.web.js --external:vscode --format=cjs --target=es2020,chrome58,edge16,firefox57",
"build-system-base": "esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node",
"build-web-base": "node esbuild.web.mjs",
"build-system-base": "node esbuild.system.mjs",
"build-base": "yarn run build-web-base && yarn run build-system-base",
"vscode:prepublish": "yarn run build-base -- --minify && yarn run build:frontend && node scripts/check-version.mjs && node scripts/postinstall.cjs && node scripts/config-man.cjs",
"compile-shared": "yarn run build:syntax && yarn run build:frontend && node scripts/check-version.mjs && node scripts/postinstall.cjs && node scripts/config-man.cjs",
"compile:web": "yarn run build-web-base -- --minify && yarn run compile-shared",
"compile:system": "yarn run build-system-base -- --minify && yarn run compile-shared",
"package": "vsce package --yarn",
"compile": "yarn run build-system-base -- --sourcemap && yarn run build:syntax && yarn run build:frontend && node scripts/postinstall.cjs",
"compile": "yarn run compile:system",
"watch": "yarn run build-system-base -- --sourcemap --watch",
"check": "tsc --noEmit",
"lint": "eslint ./src --ext .ts",
@ -1110,22 +1112,22 @@
"test": "rimraf test-dist/ && tsc -p tsconfig.test.json && node test-dist/test/runTests.js"
},
"dependencies": {
"vscode-languageclient": "^9.0.0",
"cpr": "^3.0.1",
"esbuild-plugin-polyfill-node": "^0.3.0",
"node-fetch": "^3.3.2",
"vscode-languageclient": "^9.0.0",
"ws": "^8.13.0"
},
"devDependencies": {
"@types/chai": "^4.3.16",
"@types/mocha": "^10.0.1",
"@types/node": "^20.8.10",
"@types/vscode": "^1.82.0",
"@types/chai": "^4.3.16",
"@types/ws": "^8.5.5",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"@types/ws": "^8.5.5",
"@types/mocha": "^10.0.1",
"@vscode/vsce": "^2.22.0",
"@vscode/test-electron": "^2.3.9",
"mocha": "^10.2.0",
"@vscode/vsce": "^2.22.0",
"chai": "^5.1.1",
"esbuild": "^0.19.5",
"eslint": "^8.52.0",
@ -1133,6 +1135,7 @@
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-n": "^16.2.0",
"eslint-plugin-promise": "^6.1.1",
"mocha": "^10.2.0",
"ovsx": "^0.8.3",
"typescript": "^5.2.2"
}

View file

@ -0,0 +1,147 @@
import { type ExtensionContext, commands } from "vscode";
import * as vscode from "vscode";
import { loadTinymistConfig } from "./config";
import { tinymist } from "./lsp";
import { extensionState } from "./state";
import { previewPreload } from "./features/preview";
import { onEnterHandler } from "./lsp.on-enter";
/**
* The condition
*/
type FeatureCondition = boolean;
/**
* The initialization vector
*/
type ActivationVector = (context: ExtensionContext) => void;
/**
* The initialization vector
*/
type DeactivationVector = (context: ExtensionContext) => void;
/**
* The feature entry. A conditional feature activation vector is required
* and an optional deactivation vector is also supported.
*/
export type FeatureEntry =
| [FeatureCondition, ActivationVector]
| [FeatureCondition, ActivationVector, DeactivationVector];
function configureEditorAndLanguage(context: ExtensionContext, trait: TinymistTrait) {
const isDevMode = vscode.ExtensionMode.Development == context.extensionMode;
const isWeb = extensionState.features.web;
const { config } = trait;
// Inform server that we support named completion callback at the client side
config.triggerSuggest = true;
config.triggerSuggestAndParameterHints = true;
config.triggerParameterHints = true;
config.supportHtmlInMarkdown = true;
// Sets shared features
extensionState.features.preview = !isWeb && config.previewFeature === "enable";
extensionState.features.wordSeparator = config.configureDefaultWordSeparator !== "disable";
extensionState.features.devKit = isDevMode || config.devKit === "enable";
extensionState.features.dragAndDrop = !isWeb && config.dragAndDrop === "enable";
extensionState.features.onEnter = !isWeb && !!config.onEnterEvent;
extensionState.features.renderDocs = !isWeb && config.renderDocs === "enable";
// Configures advanced editor settings to affect the host process
let configWordSeparators = async () => {
const wordSeparators = "`~!@#$%^&*()=+[{]}\\|;:'\",.<>/?";
const config1 = vscode.workspace.getConfiguration("", { languageId: "typst" });
await config1.update("editor.wordSeparators", wordSeparators, true, true);
const config2 = vscode.workspace.getConfiguration("", { languageId: "typst-code" });
await config2.update("editor.wordSeparators", wordSeparators, true, true);
};
// Runs configuration asynchronously to avoid blocking the activation
if (extensionState.features.wordSeparator) {
configWordSeparators().catch((e) =>
console.error("cannot change editor.wordSeparators for typst", e),
);
} else {
// console.log("skip configuring word separator on startup");
}
// Configures advanced language configuration
tinymist.configureLanguage(config["typingContinueCommentsOnNewline"]);
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration("tinymist.typingContinueCommentsOnNewline")) {
const config = loadTinymistConfig();
// Update language configuration
tinymist.configureLanguage(config["typingContinueCommentsOnNewline"]);
}
}),
);
}
interface TinymistTrait {
activateTable(): FeatureEntry[];
config: Record<string, any>;
}
export async function tinymistActivate(
context: ExtensionContext,
trait: TinymistTrait,
): Promise<void> {
const { activateTable, config } = trait;
tinymist.context = context;
// Sets a global context key to indicate that the extension is activated
vscode.commands.executeCommand("setContext", "ext.tinymistActivated", true);
context.subscriptions.push({
dispose: () => {
vscode.commands.executeCommand("setContext", "ext.tinymistActivated", false);
},
});
configureEditorAndLanguage(context, trait);
// Initializes language client
if (extensionState.features.lsp) {
tinymist.initClient(config);
}
// Register Shared commands
context.subscriptions.push(
commands.registerCommand("tinymist.onEnter", onEnterHandler),
commands.registerCommand("tinymist.restartServer", async () => {
await tinymistDeactivate(trait);
await tinymistActivate(context, trait);
}),
commands.registerCommand("tinymist.showLog", () => tinymist.showLog()),
);
// Activates platform-dependent features
for (const [condition, activate] of activateTable()) {
if (condition) {
activate(context);
}
}
// Starts language client
if (extensionState.features.lsp) {
await tinymist.startClient();
}
// Loads the preview HTML from the binary
if (extensionState.features.lsp && extensionState.features.preview) {
previewPreload(context);
}
return;
}
export async function tinymistDeactivate(
trait: Pick<TinymistTrait, "activateTable">,
): Promise<void> {
for (const [condition, deactivate] of trait.activateTable()) {
if (condition) {
deactivate(tinymist.context);
}
}
if (tinymist.context) {
for (const disposable of tinymist.context.subscriptions.splice(0)) {
disposable.dispose();
}
}
await tinymist.stop();
tinymist.context = undefined!;
}

View file

@ -14,24 +14,41 @@ import { loadTinymistConfig } from "./config";
import { triggerStatusBar } from "./ui-extends";
import { commandCreateLocalPackage, commandOpenLocalPackage } from "./package-manager";
import { activeTypstEditor } from "./util";
import { tinymist } from "./lsp";
import { onEnterHandler } from "./lsp.on-enter";
import { LanguageState, tinymist } from "./lsp";
import { extensionState } from "./state";
import { getUserPackageData } from "./features/tool";
import { SymbolViewProvider } from "./features/tool.symbol-view";
import { setIsTinymist as previewSetIsTinymist } from "./features/preview-compat";
import { previewActivate, previewDeactivate, previewPreload } from "./features/preview";
import { previewActivate, previewDeactivate } from "./features/preview";
import { taskActivate } from "./features/tasks";
import { devKitFeatureActivate } from "./features/dev-kit";
import { labelFeatureActivate } from "./features/label";
import { packageFeatureActivate } from "./features/package";
import { toolFeatureActivate } from "./features/tool";
import { dragAndDropActivate } from "./features/drag-and-drop";
import { FeatureEntry, tinymistActivate, tinymistDeactivate } from "./extension.shared";
import { LanguageClient } from "vscode-languageclient/node";
LanguageState.Client = LanguageClient;
const systemActivateTable = (): FeatureEntry[] => [
[extensionState.features.label, labelFeatureActivate],
[extensionState.features.package, packageFeatureActivate],
[extensionState.features.tool, toolFeatureActivate],
[extensionState.features.dragAndDrop, dragAndDropActivate],
[extensionState.features.task, taskActivate],
[extensionState.features.devKit, devKitFeatureActivate],
[extensionState.features.preview, previewActivateInTinymist, previewDeactivate],
[extensionState.features.language, languageActivate],
];
export async function activate(context: ExtensionContext): Promise<void> {
try {
return await doActivate(context);
return await tinymistActivate(context, {
activateTable: systemActivateTable,
config: loadTinymistConfig(),
});
} catch (e) {
void window.showErrorMessage(`Failed to activate tinymist: ${e}`);
throw e;
@ -39,109 +56,32 @@ export async function activate(context: ExtensionContext): Promise<void> {
}
export async function deactivate(): Promise<void> {
// Remove handlers first to avoid sending messages to the server when deactivating
previewDeactivate();
if (tinymist.context) {
for (const disposable of tinymist.context.subscriptions.splice(0)) {
disposable.dispose();
}
}
await tinymist.stop();
tinymist.context = undefined!;
tinymistDeactivate({
activateTable: systemActivateTable,
});
}
export async function doActivate(context: ExtensionContext): Promise<void> {
tinymist.context = context;
const isDevMode = vscode.ExtensionMode.Development == context.extensionMode;
// Sets a global context key to indicate that the extension is activated
vscode.commands.executeCommand("setContext", "ext.tinymistActivated", true);
context.subscriptions.push({
dispose: () => {
vscode.commands.executeCommand("setContext", "ext.tinymistActivated", false);
},
});
// Loads configuration
const config = loadTinymistConfig();
// Inform server that we support named completion callback at the client side
config.triggerSuggest = true;
config.triggerSuggestAndParameterHints = true;
config.triggerParameterHints = true;
config.supportHtmlInMarkdown = true;
// Sets features
extensionState.features.preview = config.previewFeature === "enable";
extensionState.features.wordSeparator = config.configureDefaultWordSeparator !== "disable";
extensionState.features.devKit = isDevMode || config.devKit === "enable";
extensionState.features.dragAndDrop = config.dragAndDrop === "enable";
extensionState.features.onEnter = !!config.onEnterEvent;
extensionState.features.renderDocs = config.renderDocs === "enable";
// Configures advanced editor settings to affect the host process
let configWordSeparators = async () => {
const wordSeparators = "`~!@#$%^&*()=+[{]}\\|;:'\",.<>/?";
const config1 = vscode.workspace.getConfiguration("", { languageId: "typst" });
await config1.update("editor.wordSeparators", wordSeparators, true, true);
const config2 = vscode.workspace.getConfiguration("", { languageId: "typst-code" });
await config2.update("editor.wordSeparators", wordSeparators, true, true);
};
// Runs configuration asynchronously to avoid blocking the activation
if (extensionState.features.wordSeparator) {
configWordSeparators().catch((e) =>
console.error("cannot change editor.wordSeparators for typst", e),
function previewActivateInTinymist(context: ExtensionContext) {
const typstPreviewExtension = vscode.extensions.getExtension("mgt19937.typst-preview");
if (typstPreviewExtension) {
void vscode.window.showWarningMessage(
"Tinymist Says:\n\nTypst Preview extension is already integrated into Tinymist. Please disable Typst Preview extension to avoid conflicts.",
);
} else {
// console.log("skip configuring word separator on startup");
}
// Configures advanced language configuration
tinymist.configureLanguage(config["typingContinueCommentsOnNewline"]);
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration("tinymist.typingContinueCommentsOnNewline")) {
const config = loadTinymistConfig();
// Update language configuration
tinymist.configureLanguage(config["typingContinueCommentsOnNewline"]);
}
}),
);
// Tests compat-mode preview extension
// previewActivate(context, true);
// Initializes language client
const client = tinymist.initClient(config);
// Activates features
labelFeatureActivate(context);
packageFeatureActivate(context);
toolFeatureActivate(context);
if (extensionState.features.dragAndDrop) {
dragAndDropActivate(context);
}
if (extensionState.features.task) {
taskActivate(context);
}
if (extensionState.features.devKit) {
devKitFeatureActivate(context);
}
if (extensionState.features.preview) {
const typstPreviewExtension = vscode.extensions.getExtension("mgt19937.typst-preview");
if (typstPreviewExtension) {
void vscode.window.showWarningMessage(
"Tinymist Says:\n\nTypst Preview extension is already integrated into Tinymist. Please disable Typst Preview extension to avoid conflicts.",
);
}
// Runs Integrated preview extension
previewSetIsTinymist();
previewActivate(context, false);
}
// Tests compat-mode preview extension
// previewActivate(context, true);
// Runs Integrated preview extension
previewSetIsTinymist();
previewActivate(context, false);
}
// Starts language client
languageActivate(context);
await tinymist.startClient();
// Loads the preview HTML from the binary
if (extensionState.features.preview) {
previewPreload(context);
async function languageActivate(context: ExtensionContext) {
const client = tinymist.client;
if (!client) {
console.warn("activating language feature without starting the tinymist language server");
return;
}
// Watch all non typst files.
@ -209,10 +149,6 @@ export async function doActivate(context: ExtensionContext): Promise<void> {
});
}
return;
}
async function languageActivate(context: ExtensionContext) {
context.subscriptions.push(
window.onDidChangeActiveTextEditor((editor: TextEditor | undefined) => {
if (editor?.document.isUntitled) {
@ -257,7 +193,6 @@ async function languageActivate(context: ExtensionContext) {
// prettier-ignore
context.subscriptions.push(
commands.registerCommand("tinymist.onEnter", onEnterHandler),
commands.registerCommand("tinymist.openInternal", openInternal),
commands.registerCommand("tinymist.openExternal", openExternal),
@ -265,12 +200,7 @@ async function languageActivate(context: ExtensionContext) {
commands.registerCommand("tinymist.showPdf", () => commandShow("Pdf")),
commands.registerCommand("tinymist.getCurrentDocumentMetrics", commandGetCurrentDocumentMetrics),
commands.registerCommand("tinymist.clearCache", commandClearCache),
commands.registerCommand("tinymist.restartServer", async () => {
await deactivate();
await doActivate(context);
}),
commands.registerCommand("tinymist.runCodeLens", commandRunCodeLens),
commands.registerCommand("tinymist.showLog", () => tinymist.showLog()),
commands.registerCommand("tinymist.copyAnsiHighlight", commandCopyAnsiHighlight),
commands.registerCommand("tinymist.pinMainToCurrent", () => commandPinMain(true)),

View file

@ -1,5 +1,40 @@
import { ExtensionContext } from "vscode";
import { ExtensionContext, window } from "vscode";
import { loadTinymistConfig } from "./config";
import { tinymistActivate, tinymistDeactivate } from "./extension.shared";
import { extensionState } from "./state";
export async function activate(context: ExtensionContext): Promise<void> {}
const webActivateTable = () => [];
export async function deactivate(): Promise<void> {}
export async function activate(context: ExtensionContext): Promise<void> {
extensionState.features = {
web: true,
lsp: false,
task: false,
wordSeparator: true,
label: false,
package: false,
tool: false,
devKit: false,
dragAndDrop: false,
onEnter: false,
preview: false,
language: false,
renderDocs: false,
};
try {
return await tinymistActivate(context, {
activateTable: webActivateTable,
config: loadTinymistConfig(),
});
} catch (e) {
void window.showErrorMessage(`Failed to activate tinymist: ${e}`);
throw e;
}
}
export async function deactivate(): Promise<void> {
tinymistDeactivate({
activateTable: webActivateTable,
});
}

View file

@ -0,0 +1,61 @@
// Hover storage backed by a temporary directory in the file system
import * as vscode from "vscode";
import * as crypto from "crypto";
import { Uri } from "vscode";
import { base64Decode } from "../util";
import { HoverStorageDummyHandler } from "./hover-storage";
export class HoverTmpStorage {
constructor(readonly context: vscode.ExtensionContext) {}
async startHover() {
// This is a "workspace wide" storage for temporary hover images
if (this.context.storageUri) {
const tmpImageDir = Uri.joinPath(this.context.storageUri, "tmp/hover-images/");
try {
const previousEntries = await vscode.workspace.fs.readDirectory(tmpImageDir);
let deleted = 0;
for (const [name, type] of previousEntries) {
if (type === vscode.FileType.File) {
deleted++;
await vscode.workspace.fs.delete(Uri.joinPath(tmpImageDir, name));
}
}
if (deleted > 0) {
console.log(`Deleted ${deleted} hover images`);
}
} catch {}
try {
await vscode.workspace.fs.createDirectory(tmpImageDir);
return new HoverStorageTmpFsHandler(Uri.joinPath(this.context.storageUri, "tmp/"));
} catch {}
}
return new HoverStorageDummyHandler();
}
}
class HoverStorageTmpFsHandler {
promises: PromiseLike<void>[] = [];
constructor(readonly _baseUri: vscode.Uri) {}
baseUri() {
return this._baseUri;
}
storeImage(content: string) {
const fs = vscode.workspace.fs;
const hash = crypto.createHash("sha256").update(content).digest("hex");
const tmpImagePath = `./hover-images/${hash}.svg`;
const output = Uri.joinPath(this._baseUri, tmpImagePath);
const outputContent = base64Decode(content);
this.promises.push(fs.writeFile(output, Buffer.from(outputContent, "utf-8")));
return tmpImagePath;
}
async finish() {
await Promise.all(this.promises);
}
}

View file

@ -1,7 +1,8 @@
import * as vscode from "vscode";
import * as crypto from "crypto";
import { Uri } from "vscode";
import { base64Decode } from "../util";
export interface HoverStorageProvider {
new (context: vscode.ExtensionContext): HoverStorage;
}
export interface HoverStorage {
startHover(): Promise<HoverStorageHandler>;
@ -19,61 +20,7 @@ export class HoverDummyStorage {
}
}
export class HoverTmpStorage {
constructor(readonly context: vscode.ExtensionContext) {}
async startHover() {
// This is a "workspace wide" storage for temporary hover images
if (this.context.storageUri) {
const tmpImageDir = Uri.joinPath(this.context.storageUri, "tmp/hover-images/");
try {
const previousEntries = await vscode.workspace.fs.readDirectory(tmpImageDir);
let deleted = 0;
for (const [name, type] of previousEntries) {
if (type === vscode.FileType.File) {
deleted++;
await vscode.workspace.fs.delete(Uri.joinPath(tmpImageDir, name));
}
}
if (deleted > 0) {
console.log(`Deleted ${deleted} hover images`);
}
} catch {}
try {
await vscode.workspace.fs.createDirectory(tmpImageDir);
return new HoverStorageTmpFsHandler(Uri.joinPath(this.context.storageUri, "tmp/"));
} catch {}
}
return new HoverStorageDummyHandler();
}
}
class HoverStorageTmpFsHandler {
promises: PromiseLike<void>[] = [];
constructor(readonly _baseUri: vscode.Uri) {}
baseUri() {
return this._baseUri;
}
storeImage(content: string) {
const fs = vscode.workspace.fs;
const hash = crypto.createHash("sha256").update(content).digest("hex");
const tmpImagePath = `./hover-images/${hash}.svg`;
const output = Uri.joinPath(this._baseUri, tmpImagePath);
const outputContent = base64Decode(content);
this.promises.push(fs.writeFile(output, Buffer.from(outputContent, "utf-8")));
return tmpImagePath;
}
async finish() {
await Promise.all(this.promises);
}
}
class HoverStorageDummyHandler {
export class HoverStorageDummyHandler {
baseUri() {
return undefined;
}

View file

@ -3,14 +3,15 @@ import { resolve } from "path";
import * as vscode from "vscode";
import { ExtensionMode } from "vscode";
import {
import type {
LanguageClient,
SymbolInformation,
type LanguageClientOptions,
type ServerOptions,
LanguageClientOptions,
ServerOptions,
} from "vscode-languageclient/node";
import { HoverDummyStorage, HoverTmpStorage } from "./features/hover-storage";
import { HoverDummyStorage } from "./features/hover-storage";
import type { HoverTmpStorage } from "./features/hover-storage.tmp";
import { extensionState } from "./state";
import { DisposeList, getSensibleTextEditorColumn, typstDocumentSelector } from "./util";
import { substVscodeVarsInConfig } from "./config";
@ -86,7 +87,10 @@ interface JumpInfo {
end: [number, number] | null;
}
class LanguageState {
export class LanguageState {
static Client: typeof LanguageClient = undefined!;
static HoverTmpStorage?: typeof HoverTmpStorage = undefined;
outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel("Tinymist Typst", "log");
context: vscode.ExtensionContext = undefined!;
client: LanguageClient | undefined = undefined;
@ -180,9 +184,10 @@ class LanguageState {
const trustedCommands = {
enabledCommands: ["tinymist.openInternal", "tinymist.openExternal"],
};
const hoverStorage = extensionState.features.renderDocs
? new HoverTmpStorage(context)
: new HoverDummyStorage();
const hoverStorage =
extensionState.features.renderDocs && LanguageState.HoverTmpStorage
? new LanguageState.HoverTmpStorage(context)
: new HoverDummyStorage();
const clientOptions: LanguageClientOptions = {
documentSelector: typstDocumentSelector,
@ -230,7 +235,7 @@ class LanguageState {
},
};
const client = (this.client = new LanguageClient(
const client = (this.client = new LanguageState.Client(
"tinymist",
"Tinymist Typst Language Server",
serverOptions,

View file

@ -4,12 +4,18 @@ export type ExtensionContext = vscode.ExtensionContext;
interface ExtensionState {
features: {
web: boolean;
lsp: boolean;
task: boolean;
devKit: boolean;
wordSeparator: boolean;
dragAndDrop: boolean;
label: boolean;
package: boolean;
tool: boolean;
onEnter: boolean;
preview: boolean;
language: boolean;
renderDocs: boolean;
};
mut: {
@ -22,12 +28,18 @@ interface ExtensionState {
export const extensionState: ExtensionState = {
features: {
web: false,
lsp: true,
task: true,
wordSeparator: true,
label: true,
package: true,
tool: true,
devKit: false,
dragAndDrop: false,
onEnter: false,
preview: false,
language: true,
renderDocs: false,
},
mut: {

View file

@ -5,11 +5,12 @@
import vscode = require("vscode");
import process = require("process");
import path = require("path");
import { extensionState } from "./state";
export function vscodeVariables(
string: string,
recursive?: boolean,
context = new CodeVariableContext()
context = new CodeVariableContext(),
): string {
while (true) {
string = string.replace(context.regex, (match) => {
@ -56,8 +57,12 @@ export class CodeVariableContext {
env: {
variable: true,
value: (variable: string) => {
if (extensionState.features.web) {
return "";
}
const e = variable.match(/\${env:(.*?)}/);
return (e && process.env[e[1]]) || "";
return (e && process.env?.[e[1]]) || "";
},
},
config: {
@ -162,7 +167,7 @@ export class CodeVariableContext {
const activeTextEditor = this.activeTextEditor;
if (activeTextEditor) {
selectedText = activeTextEditor.document.getText(
new vscode.Range(activeTextEditor.selection.start, activeTextEditor.selection.end)
new vscode.Range(activeTextEditor.selection.start, activeTextEditor.selection.end),
);
}

View file

@ -408,6 +408,11 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
"@jspm/core@^2.0.1":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@jspm/core/-/core-2.1.0.tgz#ee21ff64591d68de98b79ca8e4bd6c5249fded53"
integrity sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==
"@myriaddreamin/typst-ts-renderer@0.5.1":
version "0.5.1"
resolved "https://registry.yarnpkg.com/@myriaddreamin/typst-ts-renderer/-/typst-ts-renderer-0.5.1.tgz#951bb1df75c93c29b1072b71fe375fb030a8bde2"
@ -1708,6 +1713,14 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
esbuild-plugin-polyfill-node@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/esbuild-plugin-polyfill-node/-/esbuild-plugin-polyfill-node-0.3.0.tgz#e7e3804b8272df51ae4f8ebfb7445a03712504cb"
integrity sha512-SHG6CKUfWfYyYXGpW143NEZtcVVn8S/WHcEOxk62LuDXnY4Zpmc+WmxJKN6GMTgTClXJXhEM5KQlxKY6YjbucQ==
dependencies:
"@jspm/core" "^2.0.1"
import-meta-resolve "^3.0.0"
esbuild@^0.18.10:
version "0.18.20"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6"
@ -2388,6 +2401,11 @@ import-fresh@^3.2.1:
parent-module "^1.0.0"
resolve-from "^4.0.0"
import-meta-resolve@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz#75d194ae465d17c15736f414734310c87d4c45d7"
integrity sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
@ -3985,6 +4003,48 @@ tunnel@0.0.6:
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
turbo-darwin-64@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/turbo-darwin-64/-/turbo-darwin-64-2.3.3.tgz#875975cddf7abdb52b28e9cab234fc73ca001e80"
integrity sha512-bxX82xe6du/3rPmm4aCC5RdEilIN99VUld4HkFQuw+mvFg6darNBuQxyWSHZTtc25XgYjQrjsV05888w1grpaA==
turbo-darwin-arm64@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/turbo-darwin-arm64/-/turbo-darwin-arm64-2.3.3.tgz#5e85d6dadac6560782bb91081fee56f9cfcd103e"
integrity sha512-DYbQwa3NsAuWkCUYVzfOUBbSUBVQzH5HWUFy2Kgi3fGjIWVZOFk86ss+xsWu//rlEAfYwEmopigsPYSmW4X15A==
turbo-linux-64@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/turbo-linux-64/-/turbo-linux-64-2.3.3.tgz#634f2053cff6ff056bec7f307c2fb4dd9a2bc86f"
integrity sha512-eHj9OIB0dFaP6BxB88jSuaCLsOQSYWBgmhy2ErCu6D2GG6xW3b6e2UWHl/1Ho9FsTg4uVgo4DB9wGsKa5erjUA==
turbo-linux-arm64@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/turbo-linux-arm64/-/turbo-linux-arm64-2.3.3.tgz#284e26825f5d692bffb5b126a9aeccaae6a4533b"
integrity sha512-NmDE/NjZoDj1UWBhMtOPmqFLEBKhzGS61KObfrDEbXvU3lekwHeoPvAMfcovzswzch+kN2DrtbNIlz+/rp8OCg==
turbo-windows-64@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/turbo-windows-64/-/turbo-windows-64-2.3.3.tgz#2900ac2c00d9609bc480d90564a98ca1cc7f4b17"
integrity sha512-O2+BS4QqjK3dOERscXqv7N2GXNcqHr9hXumkMxDj/oGx9oCatIwnnwx34UmzodloSnJpgSqjl8iRWiY65SmYoQ==
turbo-windows-arm64@2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/turbo-windows-arm64/-/turbo-windows-arm64-2.3.3.tgz#395508cdf6b351d7dd324fb0b318c1b2cf1d66dd"
integrity sha512-dW4ZK1r6XLPNYLIKjC4o87HxYidtRRcBeo/hZ9Wng2XM/MqqYkAyzJXJGgRMsc0MMEN9z4+ZIfnSNBrA0b08ag==
turbo@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/turbo/-/turbo-2.3.3.tgz#70736263c75f7c0c501278214dee49859e1c7fcd"
integrity sha512-DUHWQAcC8BTiUZDRzAYGvpSpGLiaOQPfYXlCieQbwUvmml/LRGIe3raKdrOPOoiX0DYlzxs2nH6BoWJoZrj8hA==
optionalDependencies:
turbo-darwin-64 "2.3.3"
turbo-darwin-arm64 "2.3.3"
turbo-linux-64 "2.3.3"
turbo-linux-arm64 "2.3.3"
turbo-windows-64 "2.3.3"
turbo-windows-arm64 "2.3.3"
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"