Skip to content

Commit a1e2c08

Browse files
Merge pull request #960 from mcecode/vscode-upkeep
VS Code Extension Updates
2 parents 58135c7 + 8206f42 commit a1e2c08

File tree

8 files changed

+112
-110
lines changed

8 files changed

+112
-110
lines changed

packages/vscode-plugin/src/extension.ts

+37-15
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Executable, LanguageClientOptions } from 'vscode-languageclient/no
44
import { Uri, commands, window, workspace } from 'vscode';
55
import { LanguageClient, ResponseError, TransportKind } from 'vscode-languageclient/node';
66

7-
// There's no publicly available extension manifest type except for the internal one from VSCode's
7+
// There's no publicly available extension manifest type except for the internal one from VS Code's
88
// codebase. So, we declare our own with only the fields we need and have. See:
99
// https://stackoverflow.com/a/78536803
1010
type ExtensionManifest = {
@@ -14,7 +14,42 @@ type ExtensionManifest = {
1414

1515
let client: LanguageClient | undefined;
1616
const serverOptions: Executable = { command: '', transport: TransportKind.stdio };
17-
const clientOptions: LanguageClientOptions = {};
17+
const clientOptions: LanguageClientOptions = {
18+
middleware: {
19+
workspace: {
20+
async configuration(params, token, next) {
21+
const response = await next(params, token);
22+
23+
if (response instanceof ResponseError) {
24+
return response;
25+
}
26+
27+
return [{ 'harper-ls': response[0].harper }];
28+
},
29+
},
30+
executeCommand(command, args, next) {
31+
if (
32+
['HarperAddToUserDict', 'HarperAddToFileDict'].includes(command) &&
33+
args.find((a) => typeof a === 'string' && a.startsWith('untitled:'))
34+
) {
35+
window
36+
.showInformationMessage(
37+
'Save the file to add words to the dictionary.',
38+
'Save File',
39+
'Dismiss',
40+
)
41+
.then((selected) => {
42+
if (selected === 'Save File') {
43+
commands.executeCommand('workbench.action.files.save');
44+
}
45+
});
46+
return;
47+
}
48+
49+
next(command, args);
50+
},
51+
},
52+
};
1853

1954
export async function activate(context: ExtensionContext): Promise<void> {
2055
serverOptions.command = getExecutablePath(context);
@@ -96,19 +131,6 @@ async function startLanguageServer(): Promise<void> {
96131

97132
try {
98133
client = new LanguageClient('harper', 'Harper', serverOptions, clientOptions);
99-
100-
client.middleware.workspace = {
101-
async configuration(params, token, next) {
102-
const response = await next(params, token);
103-
104-
if (response instanceof ResponseError) {
105-
return response;
106-
}
107-
108-
return [{ 'harper-ls': response[0].harper }];
109-
},
110-
};
111-
112134
await client.start();
113135
} catch (error) {
114136
showError('Failed to start harper-ls', error);
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# Integration
22

33
This sentence has grammar errorz, like this this one.
4+
5+
On the other hand, you'll realise that this sentence doesn't have an error if you're using British English.

packages/vscode-plugin/src/tests/fixtures/integrationBritish.md

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/usr/bin/env bash
22

3-
# Errorz
43
echo "Hello World!"
4+
# Errorz

packages/vscode-plugin/src/tests/suite/helper.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,19 @@ export function createRange(
8080
return new Range(new Position(startRow, startColumn), new Position(endRow, endColumn));
8181
}
8282

83-
export function sleep(duration: number): Promise<void> {
83+
// The numbers used in these functions are what works when running tests in GitHub CI.
84+
export async function waitForHarperToActivate() {
85+
await sleep(500);
86+
}
87+
export async function waitForUpdatesFromOpenedFile() {
88+
await sleep(75);
89+
}
90+
export async function waitForUpdatesFromConfigChange() {
91+
await sleep(300);
92+
}
93+
export async function waitForUpdatesFromDeletedFile() {
94+
await sleep(450);
95+
}
96+
function sleep(duration: number): Promise<void> {
8497
return new Promise((resolve) => setTimeout(resolve, duration));
8598
}

packages/vscode-plugin/src/tests/suite/integration.test.ts

+52-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { Extension } from 'vscode';
1+
import type { Extension, Uri } from 'vscode';
22

3-
import { ConfigurationTarget, type Uri, commands, workspace } from 'vscode';
3+
import { ConfigurationTarget, commands, workspace } from 'vscode';
44

55
import {
66
activateHarper,
@@ -12,7 +12,10 @@ import {
1212
openFile,
1313
openUntitled,
1414
setTextDocumentLanguage,
15-
sleep,
15+
waitForHarperToActivate,
16+
waitForUpdatesFromConfigChange,
17+
waitForUpdatesFromDeletedFile,
18+
waitForUpdatesFromOpenedFile,
1619
} from './helper';
1720

1821
describe('Integration >', () => {
@@ -22,10 +25,8 @@ describe('Integration >', () => {
2225
beforeAll(async () => {
2326
await closeAll();
2427
harper = await activateHarper();
25-
// Open test file so diagnostics can occur
2628
markdownUri = await openFile('integration.md');
27-
// Wait for `harper-ls` to start
28-
await sleep(500);
29+
await waitForHarperToActivate();
2930
});
3031

3132
it('runs', () => {
@@ -44,15 +45,17 @@ describe('Integration >', () => {
4445
message: 'Did you mean to spell “errorz” this way?',
4546
range: createRange(2, 26, 2, 32),
4647
},
48+
{
49+
message: 'Did you mean to spell “realise” this way?',
50+
range: createRange(4, 26, 4, 33),
51+
},
4752
),
4853
);
4954
});
5055

5156
it('gives correct diagnostics for untitled', async () => {
5257
const untitledUri = await openUntitled('Errorz');
53-
54-
// Wait for `harper-ls` to send diagnostics
55-
await sleep(500);
58+
await waitForUpdatesFromOpenedFile();
5659

5760
compareActualVsExpectedDiagnostics(
5861
getActualDiagnostics(untitledUri),
@@ -68,7 +71,7 @@ describe('Integration >', () => {
6871
await setTextDocumentLanguage(untitledUri, 'plaintext');
6972

7073
// Wait for `harper-ls` to send diagnostics
71-
await sleep(500);
74+
await waitForUpdatesFromConfigChange();
7275

7376
compareActualVsExpectedDiagnostics(
7477
getActualDiagnostics(untitledUri),
@@ -87,7 +90,7 @@ describe('Integration >', () => {
8790
await setTextDocumentLanguage(untitledUri, 'shellscript');
8891

8992
// Wait for `harper-ls` to send diagnostics
90-
await sleep(500);
93+
await waitForUpdatesFromConfigChange();
9194

9295
compareActualVsExpectedDiagnostics(
9396
getActualDiagnostics(untitledUri),
@@ -101,29 +104,56 @@ describe('Integration >', () => {
101104
it('updates diagnostics on configuration change', async () => {
102105
const config = workspace.getConfiguration('harper.linters');
103106
await config.update('RepeatedWords', false, ConfigurationTarget.Workspace);
104-
// Wait for `harper-ls` to update diagnostics
105-
await sleep(300);
107+
await waitForUpdatesFromConfigChange();
106108

107109
compareActualVsExpectedDiagnostics(
108110
getActualDiagnostics(markdownUri),
109-
createExpectedDiagnostics({
110-
message: 'Did you mean to spell “errorz” this way?',
111-
range: createRange(2, 26, 2, 32),
112-
}),
111+
createExpectedDiagnostics(
112+
{
113+
message: 'Did you mean to spell “errorz” this way?',
114+
range: createRange(2, 26, 2, 32),
115+
},
116+
{
117+
message: 'Did you mean to spell “realise” this way?',
118+
range: createRange(4, 26, 4, 33),
119+
},
120+
),
113121
);
114122

115123
// Set config back to default value
116124
await config.update('RepeatedWords', true, ConfigurationTarget.Workspace);
117125
});
118126

127+
it('accepts British spellings when dialect is set to British', async () => {
128+
const config = workspace.getConfiguration('harper');
129+
await config.update('dialect', 'British', ConfigurationTarget.Workspace);
130+
await waitForUpdatesFromConfigChange();
131+
132+
compareActualVsExpectedDiagnostics(
133+
getActualDiagnostics(markdownUri),
134+
createExpectedDiagnostics(
135+
{
136+
message: 'Did you mean to repeat this word?',
137+
range: createRange(2, 39, 2, 48),
138+
},
139+
{
140+
message: 'Did you mean to spell “errorz” this way?',
141+
range: createRange(2, 26, 2, 32),
142+
},
143+
),
144+
);
145+
146+
// Set config back to default value
147+
await config.update('dialect', 'American', ConfigurationTarget.Workspace);
148+
});
149+
119150
it('updates diagnostics when files are deleted', async () => {
120151
const markdownContent = await workspace.fs.readFile(markdownUri);
121152

122-
// Delete file through VSCode
153+
// Delete file through VS Code
123154
await commands.executeCommand('workbench.files.action.showActiveFileInExplorer');
124155
await commands.executeCommand('deleteFile');
125-
// Wait for `harper-ls` to update diagnostics
126-
await sleep(450);
156+
await waitForUpdatesFromDeletedFile();
127157

128158
compareActualVsExpectedDiagnostics(
129159
getActualDiagnostics(markdownUri),
@@ -133,13 +163,11 @@ describe('Integration >', () => {
133163
// Restore and reopen deleted file
134164
await workspace.fs.writeFile(markdownUri, markdownContent);
135165
await openFile('integration.md');
136-
// Wait for `harper-ls` to update diagnostics
137-
await sleep(75);
166+
await waitForUpdatesFromOpenedFile();
138167

139168
// Delete file directly
140169
await workspace.fs.delete(markdownUri);
141-
// Wait for `harper-ls` to update diagnostics
142-
await sleep(450);
170+
await waitForUpdatesFromDeletedFile();
143171

144172
compareActualVsExpectedDiagnostics(
145173
getActualDiagnostics(markdownUri),

packages/vscode-plugin/src/tests/suite/integrationBritish.test.ts

-55
This file was deleted.

packages/vscode-plugin/src/tests/suite/languages.test.ts

+6-13
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
import {
2-
activateHarper,
32
compareActualVsExpectedDiagnostics,
43
createExpectedDiagnostics,
54
createRange,
65
getActualDiagnostics,
76
openFile,
8-
sleep,
7+
waitForUpdatesFromOpenedFile,
98
} from './helper';
109

1110
describe('Languages >', () => {
12-
beforeAll(async () => {
13-
await activateHarper();
14-
// Wait for `harper-ls` to start
15-
await sleep(500);
16-
});
11+
// NOTE: There's no need to activate Harper here since it was already activated in
12+
// `integration.test.ts`, which runs first.
1713

1814
[
1915
// Uncomment when #265 is fixed.
2016
// { type: 'JavaScript JSX', file: 'javascriptreact.jsx', row: 1, column: 36 },
21-
// Uncomment when #65 is fixed.
22-
// { type: 'Shellscript without extension', file: 'shellscript', row: 2, column: 2 },
2317

24-
// VSCode doesn't support CMake, Haskell, Literate Haskell, Nix, TOML, and Typst files out of
18+
// VS Code doesn't support CMake, Haskell, Literate Haskell, Nix, TOML, and Typst files out of
2519
// the box. Uncomment when you figure out how to support them during testing.
2620
// { type: 'CMake', file: 'CMakeLists.txt', row: 2, column: 30 },
2721
// { type: 'Haskell', file: 'haskell.hs', row: 1, column: 3 },
@@ -47,6 +41,7 @@ describe('Languages >', () => {
4741
{ type: 'Python', file: 'python.py', row: 1, column: 2 },
4842
{ type: 'Ruby', file: 'ruby.rb', row: 3, column: 16 },
4943
{ type: 'Rust', file: 'rust.rs', row: 0, column: 4 },
44+
{ type: 'Shellscript without extension', file: 'shellscript', row: 3, column: 2 },
5045
{ type: 'Shellscript with .bash extension', file: 'shellscript.bash', row: 7, column: 9 },
5146
{ type: 'Shellscript with .sh extension', file: 'shellscript.sh', row: 0, column: 22 },
5247
{ type: 'Swift', file: 'swift.swift', row: 9, column: 26 },
@@ -55,9 +50,7 @@ describe('Languages >', () => {
5550
].forEach((testCase) => {
5651
it(`gives correct diagnostics for ${testCase.type} files`, async () => {
5752
const uri = await openFile('languages', testCase.file);
58-
59-
// Wait for `harper-ls` to send diagnostics
60-
await sleep(75);
53+
await waitForUpdatesFromOpenedFile();
6154

6255
compareActualVsExpectedDiagnostics(
6356
getActualDiagnostics(uri),

0 commit comments

Comments
 (0)