Skip to content

Commit a44000b

Browse files
jdneofbricon
authored andcommitted
Enable client snippet before the language server is ready (#1101)
Signed-off-by: Sheng Chen <sheche@microsoft.com>
1 parent 236632e commit a44000b

File tree

6 files changed

+204
-11
lines changed

6 files changed

+204
-11
lines changed

package-lock.json

Lines changed: 49 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@
545545
"tslint": "tslint -p ."
546546
},
547547
"devDependencies": {
548+
"@types/fs-extra": "^8.0.0",
548549
"@types/glob": "5.0.30",
549550
"@types/lodash.findindex": "^4.6.6",
550551
"@types/mocha": "^5.2.5",
@@ -571,6 +572,7 @@
571572
"tmp": "^0.0.33",
572573
"path-exists": "^3.0.0",
573574
"expand-home-dir": "^0.0.3",
575+
"fs-extra": "^8.1.0",
574576
"glob": "^7.1.3",
575577
"winston": "^3.2.1",
576578
"winston-daily-rotate-file": "^3.10.0"

snippets/server.json

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{
2+
"sysout": {
3+
"prefix": "sysout",
4+
"body": [
5+
"System.out.println($0);"
6+
],
7+
"description": "Print to standard out"
8+
},
9+
"syserr": {
10+
"prefix": "syserr",
11+
"body": [
12+
"System.err.println($0);"
13+
],
14+
"description": "Print to standard err"
15+
},
16+
"fori": {
17+
"prefix": "fori",
18+
"body": [
19+
"for (${1:int} ${2:i} = ${3:0}; ${2:i} < ${4:max}; ${2:i}++) {",
20+
"\t$0",
21+
"}"
22+
],
23+
"description": "Indexed for loop"
24+
},
25+
"foreach": {
26+
"prefix": "foreach",
27+
"body": [
28+
"for (${1:type} ${2:var} : ${3:iterable}) {",
29+
"\t$0",
30+
"}"
31+
],
32+
"description": "Enhanced for loop"
33+
},
34+
"if": {
35+
"prefix": "if",
36+
"body": [
37+
"if (${1:condition}) {",
38+
"\t$0",
39+
"}"
40+
],
41+
"description": "if statement"
42+
},
43+
"ifelse": {
44+
"prefix": "ifelse",
45+
"body": [
46+
"if (${1:condition}) {",
47+
"\t$2",
48+
"} else {",
49+
"\t$0",
50+
"}"
51+
],
52+
"description": "if/else statement"
53+
},
54+
"ifnull": {
55+
"prefix": "ifnull",
56+
"body": [
57+
"if (${1:condition} == null) {",
58+
"\t$0",
59+
"}"
60+
],
61+
"description": "if statement checking for null"
62+
},
63+
"ifnotnull": {
64+
"prefix": "ifnotnull",
65+
"body": [
66+
"if (${1:condition} != null) {",
67+
"\t$0",
68+
"}"
69+
],
70+
"description": "if statement checking for not null"
71+
},
72+
"While Statement": {
73+
"prefix": "while",
74+
"body": [
75+
"while (${1:condition}) {",
76+
"\t$0",
77+
"}"
78+
],
79+
"description": "While Statement"
80+
},
81+
"Do-While Statement": {
82+
"prefix": "dowhile",
83+
"body": [
84+
"do {",
85+
"\t$0",
86+
"} while (${1:condition});"
87+
],
88+
"description": "Do-While Statement"
89+
}
90+
}

src/extension.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { getJavaConfiguration } from './utils';
2525
import { onConfigurationChange, excludeProjectSettingsFiles } from './settings';
2626
import { logger, initializeLogFile } from './log';
2727
import glob = require('glob');
28+
import { SnippetCompletionProvider } from './snippetCompletionProvider';
2829
import { serverTasks } from './serverTasks';
2930
import { serverTaskPresenter } from './serverTaskPresenter';
3031
import { serverStatus, ServerStatusKind } from './serverStatus';
@@ -224,6 +225,9 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
224225
const registerHoverCommand = hoverAction.registerClientHoverProvider(languageClient, context);
225226
const getDocumentSymbols: getDocumentSymbolsCommand = getDocumentSymbolsProvider(languageClient);
226227

228+
const snippetProvider: SnippetCompletionProvider = new SnippetCompletionProvider();
229+
context.subscriptions.push(languages.registerCompletionItemProvider({ scheme: 'file', language: 'java' }, snippetProvider));
230+
227231
languageClient.onReady().then(() => {
228232
languageClient.onNotification(StatusNotification.type, (report) => {
229233
switch (report.type) {
@@ -237,6 +241,7 @@ export function activate(context: ExtensionContext): Promise<ExtensionAPI> {
237241
registerHoverCommand,
238242
getDocumentSymbols
239243
});
244+
snippetProvider.setActivation(false);
240245
break;
241246
case 'Error':
242247
serverStatus.updateServerStatus(ServerStatusKind.Error);

src/snippetCompletionProvider.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
import { CompletionItemProvider, TextDocument, Position, CancellationToken, CompletionContext, CompletionItem, CompletionItemKind, SnippetString, MarkdownString } from "vscode";
4+
import * as fse from 'fs-extra';
5+
import * as path from 'path';
6+
7+
export class SnippetCompletionProvider implements CompletionItemProvider {
8+
9+
private snippets: {};
10+
private activation: boolean;
11+
12+
public constructor() {
13+
this.activation = true;
14+
this.snippets = fse.readJSONSync(path.join(__dirname, '..', 'snippets', 'server.json'));
15+
}
16+
17+
public async provideCompletionItems(_document: TextDocument, _position: Position, _token: CancellationToken, _context: CompletionContext): Promise<CompletionItem[]> {
18+
if (!this.activation) {
19+
return [];
20+
}
21+
22+
const snippetItems: CompletionItem[] = [];
23+
for (const label of Object.keys(this.snippets)) {
24+
const snippetContent = this.snippets[label];
25+
const snippetItem: CompletionItem = new CompletionItem(snippetContent.prefix);
26+
snippetItem.kind = CompletionItemKind.Snippet;
27+
snippetItem.detail = snippetContent.description;
28+
const insertText: string = (snippetContent.body as String[]).join('\n');
29+
snippetItem.insertText = new SnippetString(insertText);
30+
snippetItem.documentation = beautifyDocument(insertText);
31+
snippetItems.push(snippetItem);
32+
}
33+
return snippetItems;
34+
}
35+
36+
public setActivation(activation: boolean): void {
37+
this.activation = activation;
38+
}
39+
}
40+
41+
export function beautifyDocument(raw: string): MarkdownString {
42+
const escapedString = raw.replace(/\$\{\d:?(.*?)\}/gm, '$1').replace(/\$\d/gm, '');
43+
return new MarkdownString().appendCodeblock(escapedString, "java");
44+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as assert from 'assert';
2+
import { beautifyDocument } from '../src/snippetCompletionProvider';
3+
import { MarkdownString } from 'vscode';
4+
5+
suite('Snippet Completion Provider', () => {
6+
7+
test('should render document correctly', () => {
8+
// tslint:disable: prefer-template
9+
const raw = "for (${1:int} ${2:i} = ${3:0}; ${2:i} < ${4:max}; ${2:i}++) {\n" + "\t$0\n" + "}";
10+
const markdownString: MarkdownString = beautifyDocument(raw);
11+
const expected: string = "\n```java\n" + "for (int i = 0; i < max; i++) {\n" + "\t\n" + "}\n" + "```\n";
12+
assert.equal(markdownString.value, expected);
13+
});
14+
});

0 commit comments

Comments
 (0)