Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

## Features

- Syntax highlighting for `.hsml` files
- Diagnostics and hover via the [hsml](https://github.com/hsml-lab/hsml) language server
- Syntax highlighting for `.hsml` files and `<template lang="hsml">` in Vue SFCs
- Diagnostics, hover, and formatting via the [hsml](https://github.com/hsml-lab/hsml) language server
- Auto-download of the `hsml` binary from GitHub releases
- Comment toggling (`//` line comments)
- Bracket matching and auto-closing pairs
Expand Down
20 changes: 17 additions & 3 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { ExtensionContext } from 'vscode';
import { workspace } from 'vscode';
import { commands, workspace } from 'vscode';
import type { Executable, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node';
import { LanguageClient } from 'vscode-languageclient/node';
import { resolveServerPath } from './binary.js';

let client: LanguageClient | undefined;

export async function activate(context: ExtensionContext) {
async function startClient(context: ExtensionContext): Promise<void> {
const serverPath = await resolveServerPath(context);
if (!serverPath) {
return;
Expand Down Expand Up @@ -36,7 +36,21 @@ export async function activate(context: ExtensionContext) {
clientOptions,
);

client.start();
await client.start();
}

export async function activate(context: ExtensionContext) {
context.subscriptions.push(
commands.registerCommand('hsml.restartLanguageServer', async () => {
if (client) {
await client.restart();
} else {
await startClient(context);
}
}),
);

await startClient(context);
}

export function deactivate(): Promise<void> | undefined {
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@
"vscode-tmgrammar-test": "0.1.3"
},
"contributes": {
"commands": [
{
"command": "hsml.restartLanguageServer",
"title": "HSML: Restart Language Server"
}
],
"configuration": {
"type": "object",
"title": "HSML configuration",
Expand Down
2 changes: 2 additions & 0 deletions tests/__mocks__/vscode-languageclient-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { vi } from 'vitest';

export const mockStart = vi.fn();
export const mockStop = vi.fn(() => Promise.resolve());
export const mockRestart = vi.fn(() => Promise.resolve());

export class LanguageClient {
start = mockStart;
stop = mockStop;
restart = mockRestart;
}
3 changes: 3 additions & 0 deletions tests/__mocks__/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ export const Uri = {

export const commands = {
executeCommand: vi.fn(),
registerCommand: vi.fn((_command: string, _callback: (...args: unknown[]) => unknown) => ({
dispose: vi.fn(),
})),
};
32 changes: 32 additions & 0 deletions tests/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { resolveServerPath } from './__mocks__/binary.js';

const mockContext = {
globalStorageUri: { fsPath: '/tmp/test-storage' },
subscriptions: [],
} as unknown as ExtensionContext;

beforeEach(() => {
Expand Down Expand Up @@ -40,6 +41,37 @@ describe('activate', () => {
});
});

describe('activate', () => {
it('should register restart command', async () => {
const { commands } = await import('vscode');
const { activate } = await import('../client/src/extension.js');
await activate(mockContext);

expect(commands.registerCommand).toHaveBeenCalledWith(
'hsml.restartLanguageServer',
expect.any(Function),
);
});
});

describe('restart command', () => {
it('should call restart on existing client', async () => {
const { commands } = await import('vscode');
const { mockRestart } = await import('./__mocks__/vscode-languageclient-node.js');
const { activate } = await import('../client/src/extension.js');
await activate(mockContext);

// Get the registered callback
const registerCall = (commands.registerCommand as ReturnType<typeof vi.fn>).mock.calls.find(
(call) => call[0] === 'hsml.restartLanguageServer',
);
const restartCallback = registerCall?.[1] as () => Promise<void>;
await restartCallback();

expect(mockRestart).toHaveBeenCalled();
});
});

describe('deactivate', () => {
it('should stop the client when active', async () => {
const { mockStop } = await import('./__mocks__/vscode-languageclient-node.js');
Expand Down