Skip to content

Commit

Permalink
GH-1845: Extended the LSP with the semantic highlighting capabilities.
Browse files Browse the repository at this point in the history
Closes #1845

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
  • Loading branch information
Akos Kitta committed Jul 11, 2018
1 parent a09db28 commit e1a1818
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 39 deletions.
5 changes: 5 additions & 0 deletions packages/core/src/common/disposable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ export namespace Disposable {
}

export class DisposableCollection implements Disposable {

protected readonly disposables: Disposable[] = [];
protected readonly onDisposeEmitter = new Emitter<void>();

constructor(...toDispose: Disposable[]) {
toDispose.forEach(d => this.push(d));
}

get onDispose(): Event<void> {
return this.onDisposeEmitter.event;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/editor/src/browser/editor-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { NavigationLocationUpdater } from './navigation/navigation-location-upda
import { NavigationLocationService } from './navigation/navigation-location-service';
import { NavigationLocationSimilarity } from './navigation/navigation-location-similarity';
import { EditorVariableContribution } from './editor-variable-contribution';
import { SemanticHighlightingService } from './semantic-highlight/semantic-highlighting-service';

export default new ContainerModule(bind => {
bindEditorPreferences(bind);
Expand Down Expand Up @@ -58,4 +59,6 @@ export default new ContainerModule(bind => {
bind(NavigationLocationSimilarity).toSelf().inSingletonScope();

bind(VariableContribution).to(EditorVariableContribution).inSingletonScope();

bind(SemanticHighlightingService).toSelf().inSingletonScope();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/********************************************************************************
* Copyright (C) 2018 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { injectable } from 'inversify';
import { Position, Range } from 'vscode-languageserver-types';
import URI from '@theia/core/lib/common/uri';
import { Disposable } from '@theia/core/lib/common/disposable';

@injectable()
export class SemanticHighlightingService implements Disposable {

async decorate(uri: URI, ranges: SemanticHighlightingRange[]): Promise<void> {
// NOOP
}

dispose(): void {
// NOOP
}

}

export interface SemanticHighlightingRange extends Range {
readonly scopes: string[];
}

export { Position, Range };
33 changes: 30 additions & 3 deletions packages/java/src/browser/java-client-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,19 @@ import { CommandService } from "@theia/core/lib/common";
import {
Window, ILanguageClient, BaseLanguageClientContribution, Workspace, Languages, LanguageClientFactory, LanguageClientOptions
} from '@theia/languages/lib/browser';
import URI from '@theia/core/lib/common/uri';
import { SemanticHighlightingService, SemanticHighlightingRange, Position } from "@theia/editor/lib/browser/semantic-highlight/semantic-highlighting-service";
import { JAVA_LANGUAGE_ID, JAVA_LANGUAGE_NAME } from '../common';
import { ActionableNotification, ActionableMessage, StatusReport, StatusNotification } from "./java-protocol";
import { StatusBar, StatusBarEntry, StatusBarAlignment } from "@theia/core/lib/browser";
import {
ActionableNotification,
ActionableMessage,
StatusReport,
StatusNotification,
SemanticHighlight,
SemanticHighlightingParams,
SemanticHighlightingToken
} from "./java-protocol";

@injectable()
export class JavaClientContribution extends BaseLanguageClientContribution {
Expand All @@ -37,7 +47,8 @@ export class JavaClientContribution extends BaseLanguageClientContribution {
@inject(LanguageClientFactory) protected readonly languageClientFactory: LanguageClientFactory,
@inject(Window) protected readonly window: Window,
@inject(CommandService) protected readonly commandService: CommandService,
@inject(StatusBar) protected readonly statusBar: StatusBar
@inject(StatusBar) protected readonly statusBar: StatusBar,
@inject(SemanticHighlightingService) protected readonly semanticHighlightingService: SemanticHighlightingService
) {
super(workspace, languages, languageClientFactory);
}
Expand All @@ -53,6 +64,7 @@ export class JavaClientContribution extends BaseLanguageClientContribution {
protected onReady(languageClient: ILanguageClient): void {
languageClient.onNotification(ActionableNotification.type, this.showActionableMessage.bind(this));
languageClient.onNotification(StatusNotification.type, this.showStatusMessage.bind(this));
languageClient.onNotification(SemanticHighlight.type, this.applySemanticHighlighting.bind(this));
super.onReady(languageClient);
}

Expand All @@ -73,6 +85,20 @@ export class JavaClientContribution extends BaseLanguageClientContribution {
}, 5000);
}

protected applySemanticHighlighting(params: SemanticHighlightingParams): void {
const toRanges: (tuple: [number, SemanticHighlightingToken[]]) => SemanticHighlightingRange[] = tuple => {
const [line, tokens] = tuple;
return tokens.map(token => ({
start: Position.create(line, token.character),
end: Position.create(line, token.character + token.length),
scopes: token.scopes
}));
};
const ranges = params.lines.map(line => [line.line, line.tokens]).map(toRanges).reduce((acc, current) => acc.concat(current), []);
const uri = new URI(params.uri);
this.semanticHighlightingService.decorate(uri, ranges);
}

protected showActionableMessage(message: ActionableMessage): void {
const items = message.commands || [];
this.window.showMessage(message.severity, message.message, ...items).then(command => {
Expand All @@ -87,7 +113,8 @@ export class JavaClientContribution extends BaseLanguageClientContribution {
const options = super.createOptions();
options.initializationOptions = {
extendedClientCapabilities: {
classFileContentsSupport: true
classFileContentsSupport: true,
semanticHighlighting: true
}
};
return options;
Expand Down
20 changes: 20 additions & 0 deletions packages/java/src/browser/java-protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,30 @@ export interface ActionableMessage {
commands?: Command[];
}

export interface SemanticHighlightingParams {
readonly uri: string;
readonly lines: SemanticHighlightingInformation[];
}

export interface SemanticHighlightingInformation {
readonly line: number;
readonly tokens: SemanticHighlightingToken[];
}

export interface SemanticHighlightingToken {
readonly character: number;
readonly length: number;
readonly scopes: string[];
}

export namespace ClassFileContentsRequest {
export const type = new RequestType<TextDocumentIdentifier, string | undefined, void, void>('java/classFileContents');
}

export namespace ActionableNotification {
export const type = new NotificationType<ActionableMessage, void>('language/actionableNotification');
}

export namespace SemanticHighlight {
export const type = new NotificationType<SemanticHighlightingParams, void>('textDocument/semanticHighlighting');
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,41 @@
********************************************************************************/

import { injectable } from 'inversify';
import { JAVA_LANGUAGE_ID } from '../../common';
// import { JAVA_LANGUAGE_ID } from '../../common';
import { LanguageGrammarDefinitionContribution, TextmateRegistry } from '@theia/monaco/lib/browser/textmate';

@injectable()
export class JavaTextmateContribution implements LanguageGrammarDefinitionContribution {

registerTextmateLanguage(registry: TextmateRegistry) {
registry.registerTextMateGrammarScope('text.html.basic', {
async getGrammarDefinition() {
return {
format: 'json',
content: '{}'
};
}
});
const javaDocGrammar = require('../../../data/javadoc.tmlanguage.json');
registry.registerTextMateGrammarScope('text.html.javadoc', {
async getGrammarDefinition() {
return {
format: 'json',
content: javaDocGrammar
};
}
});
const scope = 'source.java';
const javaGrammar = require('../../../data/java.tmlanguage.json');
registry.registerTextMateGrammarScope(scope, {
async getGrammarDefinition() {
return {
format: 'json',
content: javaGrammar
};
}
});
// registry.registerTextMateGrammarScope('text.html.basic', {
// async getGrammarDefinition() {
// return {
// format: 'json',
// content: '{}'
// };
// }
// });
// const javaDocGrammar = require('../../../data/javadoc.tmlanguage.json');
// registry.registerTextMateGrammarScope('text.html.javadoc', {
// async getGrammarDefinition() {
// return {
// format: 'json',
// content: javaDocGrammar
// };
// }
// });
// const scope = 'source.java';
// const javaGrammar = require('../../../data/java.tmlanguage.json');
// registry.registerTextMateGrammarScope(scope, {
// async getGrammarDefinition() {
// return {
// format: 'json',
// content: javaGrammar
// };
// }
// });

registry.mapLanguageIdToTextmateGrammar(JAVA_LANGUAGE_ID, scope);
// registry.mapLanguageIdToTextmateGrammar(JAVA_LANGUAGE_ID, scope);
}
}
19 changes: 12 additions & 7 deletions packages/java/src/node/java-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,22 @@ export class JavaContribution extends BaseLanguageServerContribution {
}
const configurationPath = path.resolve(serverPath, configuration);
const command = 'java';
const args = [
const args: string[] = [];
let debug = DEBUG_MODE;
// TODO! must not be hard-coded.
debug = true;
if (debug) {
args.push('-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044');
}

args.push(...[
'-Declipse.application=org.eclipse.jdt.ls.core.id1',
'-Dosgi.bundles.defaultStartLevel=4',
'-Declipse.product=org.eclipse.jdt.ls.core.product'
];
]);

if (DEBUG_MODE) {
args.push(
'-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=1044',
'-Dlog.level=ALL'
);
if (debug) {
args.push('-Dlog.level=ALL');
}

args.push(
Expand Down
5 changes: 5 additions & 0 deletions packages/monaco/src/browser/monaco-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import { MonacoStrictEditorTextFocusContext } from './monaco-keybinding-contexts
import { MonacoFrontendApplicationContribution } from './monaco-frontend-application-contribution';
import MonacoTextmateModuleBinder from './textmate/monaco-textmate-frontend-bindings';
import { QuickInputService } from './monaco-quick-input-service';
import { MonacoSemanticHighlightingService } from './monaco-semantic-highlighting-service';
import { SemanticHighlightingService } from '@theia/editor/lib/browser/semantic-highlight/semantic-highlighting-service';

decorate(injectable(), MonacoToProtocolConverter);
decorate(injectable(), ProtocolToMonacoConverter);
Expand Down Expand Up @@ -94,4 +96,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
MonacoTextmateModuleBinder(bind, unbind, isBound, rebind);

bind(QuickInputService).toSelf().inSingletonScope();

bind(MonacoSemanticHighlightingService).toSelf().inSingletonScope();
rebind(SemanticHighlightingService).to(MonacoSemanticHighlightingService).inSingletonScope();
});
Loading

0 comments on commit e1a1818

Please sign in to comment.