Skip to content

Commit 91ac75a

Browse files
committed
[Qute] Add language support for qute
Fixes #178 Signed-off-by: azerr <azerr@redhat.com>
1 parent 2d5e1a9 commit 91ac75a

File tree

7 files changed

+408
-3
lines changed

7 files changed

+408
-3
lines changed

gulpfile.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ const quarkusServerExt = 'com.redhat.quarkus.ls';
2121
const quarkusExtensionDir = '../quarkus-ls/quarkus.jdt.ext';
2222
const quarkusExtension = 'com.redhat.microprofile.jdt.quarkus';
2323

24+
const quteServerDir = '../quarkus-ls/qute.ls/com.redhat.qute.ls'
25+
const quteServer = 'com.redhat.qute.ls-uber.jar';
26+
27+
const quteExtensionDir = '../quarkus-ls/qute.jdt';
28+
const quteExtension = 'com.redhat.qute.jdt';
29+
2430
gulp.task('buildServer', (done) => {
2531
cp.execSync(mvnw() + ' clean verify -DskipTests', { cwd: quarkusServerExtDir , stdio: 'inherit' });
2632
gulp.src(quarkusServerExtDir + '/target/' + quarkusServerExt + '-!(*sources).jar')
@@ -41,7 +47,22 @@ gulp.task('buildExtension', (done) => {
4147
done();
4248
});
4349

44-
gulp.task('build', gulp.series('buildServer', 'buildExtension'));
50+
gulp.task('buildQuteServer', (done) => {
51+
cp.execSync(mvnw() + ' clean install -DskipTests', { cwd: quteServerDir , stdio: 'inherit' });
52+
gulp.src(quteServerDir + '/target/' + quteServer)
53+
.pipe(gulp.dest('./server'));
54+
done();
55+
});
56+
57+
gulp.task('buildQuteExtension', (done) => {
58+
cp.execSync(mvnw() + ' -pl "' + quteExtension + '" clean verify -DskipTests', { cwd: quteExtensionDir, stdio: 'inherit' });
59+
gulp.src(quteExtensionDir + '/' + quteExtension + '/target/' + quteExtension + '-!(*sources).jar')
60+
.pipe(rename(quteExtension + '.jar'))
61+
.pipe(gulp.dest('./jars'));
62+
done();
63+
});
64+
65+
gulp.task('build', gulp.series('buildServer', 'buildExtension','buildQuteServer', 'buildQuteExtension'));
4566

4667
function mvnw() {
4768
return isWin() ? 'mvnw.cmd' : './mvnw';

package-lock.json

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

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@
5757
],
5858
"contributes": {
5959
"javaExtensions": [
60-
"./jars/com.redhat.microprofile.jdt.quarkus.jar"
60+
"./jars/com.redhat.microprofile.jdt.quarkus.jar",
61+
"./jars/com.redhat.qute.jdt.jar"
6162
],
6263
"microprofile": {
6364
"jarExtensions": [
64-
"./server/com.redhat.quarkus.ls.jar"
65+
"./server/com.redhat.quarkus.ls.jar",
66+
"./server/com.redhat.qute.ls.jar"
6567
],
6668
"documentSelector": [
6769
{
@@ -290,6 +292,8 @@
290292
"build": "./node_modules/.bin/gulp build",
291293
"build-server": "./node_modules/.bin/gulp buildServer",
292294
"build-ext": "./node_modules/.bin/gulp buildExtension",
295+
"build-qute-server": "./node_modules/.bin/gulp buildQuteServer",
296+
"build-qute-ext": "./node_modules/.bin/gulp buildQuteExtension",
293297
"test-ui": "rm -rf out && npm run test-compile && npm run test-ui-run",
294298
"test-ui-run": "extest setup-and-run 'out/test/vscodeUiTest/suite/*.js' -u -e 'out/test/vscodeUiTest/extensions'",
295299
"test-all": "npm test && npm run test-ui-run"
@@ -334,13 +338,15 @@
334338
"@redhat-developer/vscode-redhat-telemetry": "^0.4.2",
335339
"ejs": "^2.7.1",
336340
"expand-home-dir": "0.0.3",
341+
"find-java-home": "^1.0.0",
337342
"find-up": "^4.1.0",
338343
"fs-extra": "^8.0.1",
339344
"glob": "^7.1.4",
340345
"js-yaml": "^4.0.0",
341346
"lodash": "^4.17.21",
342347
"request": "^2.88.0",
343348
"request-promise": "^4.2.4",
349+
"vscode-languageclient": "^7.0.0",
344350
"which": "^2.0.2",
345351
"yauzl": "^2.10.0"
346352
}

src/extension.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ProjectLabelInfo } from './definitions/ProjectLabelInfo';
2020
import { PropertiesLanguageMismatch, QuarkusConfig } from './QuarkusConfig';
2121
import { QuarkusContext } from './QuarkusContext';
2222
import quarkusProjectListener from './QuarkusProjectListener';
23+
import { connectToQuteLS } from './qute/languageServer/client';
2324
import { terminalCommandRunner } from './terminal/terminalCommandRunner';
2425
import { initTelemetryService } from './utils/telemetryUtils';
2526
import { WelcomeWebview } from './webviews/WelcomeWebview';
@@ -64,6 +65,14 @@ export async function activate(context: ExtensionContext) {
6465
);
6566

6667
registerVSCodeCommands(context);
68+
69+
connectToQuteLS(context).catch((error) => {
70+
window.showErrorMessage(error.message, error.label).then((selection) => {
71+
if (error.label && error.label === selection && error.openUrl) {
72+
commands.executeCommand('vscode.open', error.openUrl);
73+
}
74+
});
75+
});
6776
}
6877

6978
export function deactivate() {

src/qute/languageServer/client.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import * as requirements from './requirements';
2+
3+
import { DidChangeConfigurationNotification, LanguageClientOptions } from 'vscode-languageclient';
4+
import { LanguageClient } from 'vscode-languageclient/node';
5+
import { ExtensionContext, commands, window, workspace, Uri } from 'vscode';
6+
import { prepareExecutable } from './javaServerStarter';
7+
8+
export function connectToQuteLS(context: ExtensionContext) {
9+
return requirements.resolveRequirements().then(requirements => {
10+
const clientOptions: LanguageClientOptions = {
11+
documentSelector: [
12+
{ scheme: 'file', language: 'qute-html' },
13+
{ scheme: 'file', language: 'qute-json' },
14+
{ scheme: 'file', language: 'qute-yaml' },
15+
{ scheme: 'file', language: 'qute-txt' },
16+
{ scheme: 'untitled', language: 'qute-html' },
17+
{ scheme: 'vscode-notebook-cell', language: 'qute-html' },
18+
{ scheme: 'file', language: 'java' }
19+
],
20+
// wrap with key 'settings' so it can be handled same a DidChangeConfiguration
21+
initializationOptions: {
22+
settings: getQuteSettings()
23+
},
24+
synchronize: {
25+
// preferences starting with these will trigger didChangeConfiguration
26+
configurationSection: ['quarkus', '[qute]']
27+
},
28+
middleware: {
29+
workspace: {
30+
didChangeConfiguration: () => {
31+
quteLanguageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getQuteSettings() });
32+
}
33+
}
34+
}
35+
};
36+
37+
function bindQuteRequest(request: string) {
38+
quteLanguageClient.onRequest(request, async (params: any) =>
39+
<any>await commands.executeCommand("java.execute.workspaceCommand", request, params)
40+
);
41+
}
42+
43+
function bindQuteNotification(notification: string) {
44+
context.subscriptions.push(commands.registerCommand(notification, (event: any) => {
45+
quteLanguageClient.sendNotification(notification, event);
46+
}));
47+
}
48+
49+
const serverOptions = prepareExecutable(requirements);
50+
const quteLanguageClient = new LanguageClient('qute', 'Qute Support', serverOptions, clientOptions);
51+
context.subscriptions.push(quteLanguageClient.start());
52+
return quteLanguageClient.onReady().then(() => {
53+
bindQuteRequest('qute/template/project');
54+
bindQuteRequest('qute/template/projectDataModel');
55+
bindQuteRequest('qute/template/javaClasses');
56+
bindQuteRequest('qute/template/resolvedJavaClass');
57+
bindQuteRequest('qute/template/javaDefinition');
58+
bindQuteNotification('qute/dataModelChanged');
59+
bindQuteRequest('qute/java/codeLens');
60+
}
61+
)
62+
});
63+
}
64+
65+
/**
66+
* Returns a json object with key 'quarkus' and a json object value that
67+
* holds all quarkus. settings.
68+
*
69+
* Returns: {
70+
* 'quarkus': {...}
71+
* }
72+
*/
73+
function getQuteSettings(): JSON {
74+
const configQuarkus = workspace.getConfiguration().get('quarkus');
75+
let quarkus;
76+
if (!configQuarkus) { // Set default preferences if not provided
77+
const defaultValue =
78+
{
79+
qute: {
80+
81+
}
82+
};
83+
quarkus = defaultValue;
84+
} else {
85+
const x = JSON.stringify(configQuarkus); // configQuarkus is not a JSON type
86+
quarkus = { quarkus: JSON.parse(x) };
87+
}
88+
return quarkus;
89+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import * as os from 'os';
2+
import * as path from 'path';
3+
import { workspace } from 'vscode';
4+
import { Executable, ExecutableOptions } from 'vscode-languageclient/node';
5+
import { RequirementsData } from './requirements';
6+
import * as glob from 'glob';
7+
8+
const DEBUG = startedInDebugMode();
9+
const DEBUG_PORT = 1074;
10+
const QUTE_SERVER_NAME = 'com.redhat.qute.ls-uber.jar';
11+
const QUTE_SERVER_MAIN_CLASS = 'com.redhat.qute.ls.QuteServerLauncher';
12+
13+
export function prepareExecutable(requirements: RequirementsData): Executable {
14+
const executable: Executable = Object.create(null);
15+
const options: ExecutableOptions = Object.create(null);
16+
options.env = process.env;
17+
executable.options = options;
18+
executable.command = path.resolve(requirements.java_home + '/bin/java');
19+
executable.args = prepareParams();
20+
return executable;
21+
}
22+
23+
function prepareParams(): string[] {
24+
const params: string[] = [];
25+
if (DEBUG) {
26+
if (process.env.SUSPEND_SERVER === 'true') {
27+
params.push(`-agentlib:jdwp=transport=dt_socket,server=y,address=${DEBUG_PORT}`);
28+
} else {
29+
params.push(`-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=${DEBUG_PORT},quiet=y`);
30+
}
31+
}
32+
33+
const vmargs = workspace.getConfiguration("quarkus.tools").get("server.vmargs", '');
34+
if (os.platform() === 'win32') {
35+
const watchParentProcess = '-DwatchParentProcess=';
36+
if (vmargs.indexOf(watchParentProcess) < 0) {
37+
params.push(watchParentProcess + 'false');
38+
}
39+
}
40+
parseVMargs(params, vmargs);
41+
const serverHome: string = path.resolve(__dirname, '../server');
42+
const microprofileServerFound: Array<string> = glob.sync(`**/${QUTE_SERVER_NAME}`, { cwd: serverHome });
43+
if (microprofileServerFound.length) {
44+
let mpJavaExtensionsClasspath = microprofileServerFound[0];
45+
params.push('-cp');
46+
params.push(`${serverHome}/` + mpJavaExtensionsClasspath);
47+
params.push(QUTE_SERVER_MAIN_CLASS);
48+
} else {
49+
throw new Error('Unable to find required Language Server JARs');
50+
}
51+
return params;
52+
}
53+
54+
function hasDebugFlag(args: string[]): boolean {
55+
if (args) {
56+
// See https://nodejs.org/en/docs/guides/debugging-getting-started/
57+
return args.some(arg => /^--inspect/.test(arg) || /^--debug/.test(arg));
58+
}
59+
return false;
60+
}
61+
62+
function startedInDebugMode(): boolean {
63+
const args: string[] = process.execArgv;
64+
return hasDebugFlag(args);
65+
}
66+
67+
// exported for tests
68+
export function parseVMargs(params: string[], vmargsLine: string): void {
69+
if (!vmargsLine) {
70+
return;
71+
}
72+
const vmargs = vmargsLine.match(/(?:[^\s"]+|"[^"]*")+/g);
73+
if (vmargs === null) {
74+
return;
75+
}
76+
vmargs.forEach(arg => {
77+
// remove all standalone double quotes
78+
arg = arg.replace(/(\\)?"/g, ($0, $1) => { return ($1 ? $0 : ''); });
79+
// unescape all escaped double quotes
80+
arg = arg.replace(/(\\)"/g, '"');
81+
if (params.indexOf(arg) < 0) {
82+
params.push(arg);
83+
}
84+
});
85+
}

0 commit comments

Comments
 (0)