Skip to content

Commit

Permalink
Prepare webview setup
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist committed Jan 25, 2024
1 parent 2e4a294 commit 312ee51
Show file tree
Hide file tree
Showing 12 changed files with 440 additions and 111 deletions.
6 changes: 3 additions & 3 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"@types/jest": "^27.4.0",
"@types/node": "^16.0.1",
"esbuild": "^0.19.9",
"jest": "^27.4.7"
"jest": "^27.4.7",
"xstate5": "npm:xstate@^5.5.2"
},
"dependencies": {
"@babel/core": "^7.21.4",
Expand All @@ -27,8 +28,7 @@
"dotenv": "^16.0.3",
"isomorphic-fetch": "^3.0.0",
"prettier": "^2.8.8",
"xstate": "^4.33.4",
"xstate-beta": "npm:xstate@beta"
"xstate": "^4.33.4"
},
"author": "Stately Team",
"license": "MIT"
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/__tests__/__sky__/actorFromStately.sky.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file was generated by the XState CLI, please do not edit it manually.
import { createMachine } from 'xstate-beta';
import { createMachine } from 'xstate5';

const machine = createMachine(
{
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/__tests__/__sky__/machineFromStately.sky.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file was generated by the XState CLI, please do not edit it manually.
import { createMachine } from 'xstate-beta';
import { createMachine } from 'xstate5';

const machine = createMachine(
{
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/__tests__/__sky__/machineFromStately.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { machineFromStately } from '@statelyai/sky';
import { createActor } from 'xstate-beta';
import { createActor } from 'xstate5';
import { skyConfig } from './machineFromStately.sky';

// TODO: The Sky SDK is using xstate v5 so we can't really create meaningful test until we upgrade to v5 in this repo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"settings": {
"git.openRepositoryInParentFolders": "never",
"xstate.server.enable": true
"xstate.server.enabled": true
},
"folders": [{ "path": "basic" }]
}
5 changes: 3 additions & 2 deletions new-packages/vscode-xstate/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"@volar/language-server": "^2.0.0",
"@volar/vscode": "^2.0.0",
"@xstate/language-server": "0.0.0",
"vscode-languageserver-protocol": "^3.17.5"
"vscode-languageserver-protocol": "^3.17.5",
"xstate": "npm:xstate@^5.5.2"
},
"devDependencies": {
"esbuild": "^0.19.9"
Expand All @@ -31,7 +32,7 @@
{
"title": "XState",
"properties": {
"xstate.server.enable": {
"xstate.server.enabled": {
"type": "boolean",
"default": true
}
Expand Down
190 changes: 92 additions & 98 deletions new-packages/vscode-xstate/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,112 +1,106 @@
import { getTsdk } from '@volar/vscode';
import { LanguageClient, TransportKind } from '@volar/vscode/node.js';
import { helloRequest } from '@xstate/language-server/protocol';
import * as vscode from 'vscode';
import { RequestType } from 'vscode-languageserver-protocol';
import { assertEvent, assign, createActor, fromPromise, setup } from 'xstate';
import { languageClientMachine } from './languageClient';
import { assertDefined, assertExtendedEvent } from './utils';

let client: LanguageClient | undefined;
let disposable: vscode.Disposable | undefined;

export async function activate(context: vscode.ExtensionContext) {
const { tsdk } = await getTsdk(context);

client = new LanguageClient(
'XState',
{
module: context.asAbsolutePath('dist/language-server.js'),
transport: TransportKind.ipc,
},
{
documentSelector: [{ language: 'typescript' }],
initializationOptions: {
typescript: { tsdk },
const extensionMachine = setup({
types: {} as {
context: {
extensionContext: vscode.ExtensionContext | undefined;
};
events:
| { type: 'ACTIVATE'; extensionContext: vscode.ExtensionContext }
| { type: 'DEACTIVATE' };
},
actions: {
assignExtensionContext: assign(({ event }) => {
assertEvent(event, 'ACTIVATE');
return { extensionContext: event.extensionContext };
}),
},
actors: {
getTsdk: fromPromise(
async ({
input: extensionContext,
}: {
input: vscode.ExtensionContext;
}) => ({ tsdk: (await getTsdk(extensionContext)).tsdk }),
),
languageClient: languageClientMachine,
},
}).createMachine({
context: {
extensionContext: undefined,
},
initial: 'idle',
states: {
idle: {
on: {
ACTIVATE: {
target: 'active',
actions: 'assignExtensionContext',
},
},
},
);

tryRestartServer(context);

context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((event) => {
if (event.affectsConfiguration('xstate.server.enable')) {
tryRestartServer(context);
}
}),
);

context.subscriptions.push(
vscode.commands.registerCommand('xstate.hello', () => {
const activeTextEditorUri =
vscode.window.activeTextEditor?.document.uri.toString();
sendRequest(helloRequest, {
name: 'Andarist',
textDocument: activeTextEditorUri
? {
uri: activeTextEditorUri,
}
: undefined,
});
}),
);
}
active: {
initial: 'starting',
states: {
starting: {
invoke: {
src: 'getTsdk',
id: 'getTsdk',
input: ({ context }) => {
assertDefined(context.extensionContext);
return context.extensionContext;
},
onDone: {
target: 'running',
},
},
},
running: {
invoke: {
src: 'languageClient',
input: ({ context, event }) => {
assertDefined(context.extensionContext);

export async function deactivate() {
await stopServer();
}
const extendedEvent = assertExtendedEvent<
typeof event,
{
type: 'xstate.done.actor.getTsdk';
output: Pick<Awaited<ReturnType<typeof getTsdk>>, 'tsdk'>;
}
>(event, 'xstate.done.actor.getTsdk');

async function startServer(_: vscode.ExtensionContext) {
const currentClient = client;
if (!currentClient) {
return;
}
if (currentClient.needsStart()) {
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Window,
title: 'Starting XState Language Server…',
return {
extensionContext: context.extensionContext,
tsdk: extendedEvent.output.tsdk,
isServerEnabled:
vscode.workspace
.getConfiguration('xstate')
.get('server.enabled') ?? true,
};
},
},
},
},
async () => {
await currentClient.start();
// this `disposable` acts as a placeholder in case some extra cleanup has to be done here in the future
disposable = new vscode.Disposable(() => {});
on: {
DEACTIVATE: 'deactivated',
},
);
}
}

async function stopServer() {
const currentClient = client;
if (!currentClient) {
return;
}
if (currentClient.needsStop()) {
disposable?.dispose();
},
deactivated: {
type: 'final',
},
},
});

await currentClient.stop();
}
}
const extensionActorRef = createActor(extensionMachine).start();

async function tryRestartServer(context: vscode.ExtensionContext) {
await stopServer();
if (vscode.workspace.getConfiguration('xstate').get('server.enable')) {
await startServer(context);
}
export async function activate(context: vscode.ExtensionContext) {
extensionActorRef.send({ type: 'ACTIVATE', extensionContext: context });
}

/**
* A wrapper function that proxies to the `client.sendRequest`.
* It's only purpose is to provide better types since `client.sendRequest` has 6 overloads
* and reports poor error messages partially cause of that.
*
* This mitigates the problem by defining 1 focused signature.
*/
function sendRequest<P, R, E>(
request: RequestType<P, R, E>,
params: P,
): Promise<R> | undefined {
const currentClient = client;
if (!currentClient) {
return;
}
return currentClient.sendRequest(request, params);
export async function deactivate() {
extensionActorRef.send({ type: 'DEACTIVATE' });
}
Loading

0 comments on commit 312ee51

Please sign in to comment.