Skip to content

Commit e881846

Browse files
author
Akos Kitta
committed
Initial API of the semantic highlighting protocol extension.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
1 parent 6d8ddcc commit e881846

5 files changed

Lines changed: 287 additions & 2 deletions

File tree

‎client/src/main.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { ConfigurationFeature as PullConfigurationFeature} from './configuration
2525
import { ImplementationFeature } from './implementation'
2626
import { TypeDefinitionFeature } from './typeDefinition';
2727
import { WorkspaceFoldersFeature } from './workspaceFolders';
28+
// import { SemanticHighlightingFeature } from './semanticHighlighting.proposed';
2829

2930
import * as Is from './utils/is';
3031
import * as electron from './utils/electron';
@@ -493,6 +494,7 @@ export class SettingMonitor {
493494
export namespace ProposedFeatures {
494495
export function createAll(_client: BaseLanguageClient): (StaticFeature | DynamicFeature<any>)[] {
495496
let result: (StaticFeature | DynamicFeature<any>)[] = [];
497+
// result.push(new SemanticHighlightingFeature(_client));
496498
return result;
497499
}
498500
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) TypeFox. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
'use strict';
6+
7+
import { Disposable, Uri, Range, window, DecorationRenderOptions, TextEditorDecorationType, workspace, TextEditor } from 'vscode';
8+
import {
9+
TextDocumentRegistrationOptions, ClientCapabilities, ServerCapabilities, DocumentSelector, NotificationHandler,
10+
SemanticHighlightingNotification, SemanticHighlightingParams, SemanticHighlightingInformation
11+
} from 'vscode-languageserver-protocol';
12+
13+
import * as UUID from './utils/uuid';
14+
import { TextDocumentFeature, BaseLanguageClient } from './client';
15+
16+
export class SemanticHighlightingFeature extends TextDocumentFeature<TextDocumentRegistrationOptions> {
17+
18+
protected readonly toDispose: Disposable[];
19+
protected readonly decorations: Map<string, any>;
20+
protected readonly handlers: NotificationHandler<SemanticHighlightingParams>[];
21+
22+
constructor(client: BaseLanguageClient) {
23+
super(client, SemanticHighlightingNotification.type);
24+
this.toDispose = [];
25+
this.decorations = new Map();
26+
this.handlers = [];
27+
this.toDispose.push({ dispose: () => this.decorations.clear() });
28+
this.toDispose.push(workspace.onDidCloseTextDocument(e => {
29+
const uri = e.uri.toString();
30+
if (this.decorations.has(uri)) {
31+
// TODO: do the proper disposal of the decorations.
32+
this.decorations.delete(uri);
33+
}
34+
}));
35+
}
36+
37+
dispose(): void {
38+
this.toDispose.forEach(disposable => disposable.dispose());
39+
super.dispose();
40+
}
41+
42+
fillClientCapabilities(capabilities: ClientCapabilities): void {
43+
if (!!capabilities.textDocument) {
44+
capabilities.textDocument = {};
45+
}
46+
capabilities.textDocument!.semanticHighlighting = true;
47+
}
48+
49+
initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
50+
console.log('TODO: Currently, the server capabilities are ignored.', capabilities);
51+
if (documentSelector) {
52+
const id = UUID.generateUuid();
53+
this.register(this.messages, {
54+
id,
55+
registerOptions: Object.assign({}, { documentSelector })
56+
});
57+
}
58+
}
59+
60+
protected registerLanguageProvider(options: TextDocumentRegistrationOptions): Disposable {
61+
if (options.documentSelector === null) {
62+
return new Disposable(() => { });
63+
}
64+
const handler = this.newNotificationHandler.bind(this)();
65+
this._client.onNotification(SemanticHighlightingNotification.type, handler);
66+
return new Disposable(() => {
67+
const indexOf = this.handlers.indexOf(handler);
68+
if (indexOf !== -1) {
69+
this.handlers.splice(indexOf, 1);
70+
}
71+
})
72+
}
73+
74+
protected newNotificationHandler(): NotificationHandler<SemanticHighlightingParams> {
75+
return (params: SemanticHighlightingParams) => {
76+
const editorPredicate = this.editorPredicate(params.uri);
77+
window.visibleTextEditors.filter(editorPredicate).forEach(editor => this.applyDecorations(editor, params));
78+
};
79+
}
80+
81+
protected editorPredicate(uri: string): (editor: TextEditor) => boolean {
82+
const predicateUri = Uri.parse(uri);
83+
return (editor: TextEditor) => editor.document.uri.toString() === predicateUri.toString();
84+
}
85+
86+
protected applyDecorations(editor: TextEditor, params: SemanticHighlightingParams): void {
87+
console.log('TODO: Apply the decorations on the editor.', editor, params);
88+
}
89+
90+
protected decorationType(options: DecorationRenderOptions = {}) {
91+
return window.createTextEditorDecorationType(options);
92+
}
93+
94+
protected map2Decoration(lines: SemanticHighlightingInformation[]): [TextEditorDecorationType, Range[]] {
95+
console.log('TODO: Map the lines (and the tokens) to the desired decoration type.', lines);
96+
return [this.decorationType(), []];
97+
}
98+
99+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#### Semantic Highlighting
2+
3+
While the syntax highlighting are done on the client-side and can handle keywords, strings, and other low-level tokens from the grammar, it cannot adequately support complex coloring. Semantic highlighting information is calculated on the language server and pushed to the client as a notification. This notification carries information about the ranges that have to be colored. The desired coloring details are given as [TextMate scopes](https://manual.macromates.com/en/language_grammars) for each affected range. For the semantic highlighting information the following additions are proposed:
4+
5+
_Client Capabilities_:
6+
7+
Capability that has to be set by the language client if it can accept and process the semantic highlighting information received from the server.
8+
9+
```ts
10+
/**
11+
* The text document client capabilities.
12+
*/
13+
textDocument: {
14+
15+
/**
16+
* `true` if the client supports semantic highlighting support text documents. Otherwise, `false`. It is `false` by default.
17+
*/
18+
semanticHighlighting?: boolean;
19+
}
20+
```
21+
22+
##### SemanticHighlighting Notification
23+
24+
The `textDocument/semanticHighlighting` notification is pushed from the server to the client to inform the client about additional semantic highlighting information that has to be applied on the text document. It is the server's responsibility to decide which lines are included in the highlighting information. In other words, the server is capable of sending only a delta information. For instance, after opening the text document (`DidOpenTextDocumentNotification`) the server sends the semantic highlighting information for the entire document, but if the server receives a `DidChangeTextDocumentNotification`, it pushes the information only about the affected lines in the document.
25+
26+
_Notification_:
27+
28+
* method: 'workspace/semanticHighlighting'
29+
* params: `SemanticHighlightingParams` defined as follows:
30+
31+
```ts
32+
/**
33+
* Parameters for the semantic highlighting (server-side) push notification.
34+
*/
35+
export interface SemanticHighlightingParams {
36+
37+
/**
38+
* The URI for which semantic highlighting information is reported to.
39+
*/
40+
uri: string;
41+
42+
/**
43+
* An array of semantic highlighting information.
44+
*/
45+
lines: SemanticHighlightingInformation[];
46+
47+
}
48+
49+
/**
50+
* Represents a semantic highlighting information that has to be applied on a specific line of the text document.
51+
*/
52+
export interface SemanticHighlightingInformation {
53+
54+
/**
55+
* The zero-based line position in the text document.
56+
*/
57+
line: number;
58+
59+
/**
60+
* An array of highlighting tokens for the current line in the text document.
61+
*/
62+
tokens: SemanticHighlightingToken[];
63+
64+
}
65+
66+
/**
67+
* Representation of a single semantic highlighting token that has to be applied on a line of the text document.
68+
*/
69+
export interface SemanticHighlightingToken {
70+
71+
/**
72+
* The zero-based character offset on the line in a text document where this token applies. If negative, defaults to `0`.
73+
* If greater than the length of the current line, defaults to the end of the line.
74+
*/
75+
character: number;
76+
77+
/**
78+
* The length of the token. If negative, defaults to `0`. If the sum of the `character` and the `length` exceeds the length of the line,
79+
* it defaults back to the end of the line.
80+
*/
81+
length: number;
82+
83+
/**
84+
* An array of semantic highlighting [TextMate scopes](https://manual.macromates.com/en/language_grammars) for the current token.
85+
*/
86+
scopes: string[];
87+
88+
}
89+
```
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) TypeFox. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
'use strict';
6+
7+
import { NotificationType } from 'vscode-jsonrpc';
8+
9+
/**
10+
* Parameters for the semantic highlighting (server-side) push notification.
11+
*/
12+
export interface SemanticHighlightingParams {
13+
14+
/**
15+
* The URI for which semantic highlighting information is reported to.
16+
*/
17+
uri: string;
18+
19+
/**
20+
* An array of semantic highlighting information.
21+
*/
22+
lines: SemanticHighlightingInformation[];
23+
24+
}
25+
26+
/**
27+
* Represents a semantic highlighting information that has to be applied on a specific line of the text document.
28+
*/
29+
export interface SemanticHighlightingInformation {
30+
31+
/**
32+
* The zero-based line position in the text document.
33+
*/
34+
line: number;
35+
36+
/**
37+
* An array of highlighting tokens for the current line in the text document.
38+
*/
39+
tokens: SemanticHighlightingToken[];
40+
41+
}
42+
43+
/**
44+
* Representation of a single semantic highlighting token that has to be applied on a line of the text document.
45+
*/
46+
export interface SemanticHighlightingToken {
47+
48+
/**
49+
* The zero-based character offset on the line in a text document where this token applies. If negative, defaults to `0`.
50+
* If greater than the length of the current line, defaults to the end of the line.
51+
*/
52+
character: number;
53+
54+
/**
55+
* The length of the token. If negative, defaults to `0`. If the sum of the `character` and the `length` exceeds the length of the line,
56+
* it defaults back to the end of the line.
57+
*/
58+
length: number;
59+
60+
/**
61+
* An array of semantic highlighting [TextMate scopes](https://manual.macromates.com/en/language_grammars) for the current token.
62+
*/
63+
scopes: string[];
64+
65+
}
66+
67+
/**
68+
* Language server push notification providing the semantic highlighting information for a text document.
69+
*/
70+
export namespace SemanticHighlightingNotification {
71+
export const type = new NotificationType<SemanticHighlightingParams, void>('textDocument/semanticHighlighting');
72+
}
73+
74+
/**
75+
* Capability that has to be set by the language client if that supports the semantic highlighting feature for the text documents.
76+
*/
77+
export interface SemanticHighlightingClientCapabilities {
78+
79+
/**
80+
* The text document client capabilities.
81+
*/
82+
textDocument?: {
83+
84+
/**
85+
* `true` if the client supports semantic highlighting support text documents. Otherwise, `false`. It is `false` by default.
86+
*/
87+
semanticHighlighting?: boolean;
88+
89+
}
90+
}

‎protocol/src/protocol.ts‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ import {
3030
Color, ColorInformation, ColorPresentation, ColorServerCapabilities, ColorClientCapabilities,
3131
} from './protocol.colorProvider';
3232

33+
import {
34+
SemanticHighlightingNotification, SemanticHighlightingParams, SemanticHighlightingInformation, SemanticHighlightingToken, SemanticHighlightingClientCapabilities
35+
} from './protocol.semanticHighlighting.proposed';
36+
3337
/**
3438
* A document filter denotes a document by different properties like
3539
* the [language](#TextDocument.languageId), the [scheme](#Uri.scheme) of
@@ -545,7 +549,7 @@ export interface _ClientCapabilities {
545549
}
546550

547551
export type ClientCapabilities = _ClientCapabilities & ImplementationClientCapabilities & TypeDefinitionClientCapabilities &
548-
WorkspaceFoldersClientCapabilities & ConfigurationClientCapabilities & ColorClientCapabilities;
552+
WorkspaceFoldersClientCapabilities & ConfigurationClientCapabilities & ColorClientCapabilities & SemanticHighlightingClientCapabilities;
549553

550554
/**
551555
* Defines how the host (editor) should sync
@@ -1786,5 +1790,6 @@ export {
17861790
TypeDefinitionRequest,
17871791
WorkspaceFoldersRequest, DidChangeWorkspaceFoldersNotification, DidChangeWorkspaceFoldersParams, WorkspaceFolder, WorkspaceFoldersChangeEvent,
17881792
ConfigurationRequest, ConfigurationParams, ConfigurationItem,
1789-
DocumentColorRequest, ColorPresentationRequest, ColorProviderOptions, DocumentColorParams, ColorPresentationParams, Color, ColorInformation, ColorPresentation
1793+
DocumentColorRequest, ColorPresentationRequest, ColorProviderOptions, DocumentColorParams, ColorPresentationParams, Color, ColorInformation, ColorPresentation,
1794+
SemanticHighlightingNotification, SemanticHighlightingParams, SemanticHighlightingInformation, SemanticHighlightingToken
17901795
};

0 commit comments

Comments
 (0)