Skip to content

Commit 61a2adf

Browse files
committed
[json] setting for syntax folding (experimental)
1 parent 71da90f commit 61a2adf

6 files changed

Lines changed: 235 additions & 122 deletions

File tree

extensions/json/client/src/jsonMain.ts

Lines changed: 37 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import * as path from 'path';
88
import * as nls from 'vscode-nls';
99
const localize = nls.loadMessageBundle();
1010

11-
import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration, TextDocument, FoldingRangeList as VSFoldingRangeList, FoldingRange as VSFoldingRange } from 'vscode';
12-
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, TextDocumentIdentifier } from 'vscode-languageclient';
11+
import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration, TextDocument, FoldingRangeList, FoldingRange, Disposable } from 'vscode';
12+
import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient';
1313
import TelemetryReporter from 'vscode-extension-telemetry';
1414

15+
import { FoldingRangesRequest } from './protocol/foldingProvider.proposed';
16+
1517
import { hash } from './utils/hash';
1618

1719
namespace VSCodeContentRequest {
@@ -30,57 +32,6 @@ namespace SchemaAssociationNotification {
3032
export const type: NotificationType<ISchemaAssociations, any> = new NotificationType('json/schemaAssociations');
3133
}
3234

33-
interface FoldingRangeList {
34-
/**
35-
* The folding ranges.
36-
*/
37-
ranges: FoldingRange[];
38-
}
39-
40-
export enum FoldingRangeType {
41-
/**
42-
* Folding range for a comment
43-
*/
44-
Comment = 'comment',
45-
/**
46-
* Folding range for a imports or includes
47-
*/
48-
Imports = 'imports',
49-
/**
50-
* Folding range for a region (e.g. `#region`)
51-
*/
52-
Region = 'region'
53-
}
54-
55-
interface FoldingRange {
56-
57-
/**
58-
* The start line number
59-
*/
60-
startLine: number;
61-
62-
/**
63-
* The end line number
64-
*/
65-
endLine: number;
66-
67-
/**
68-
* The actual color value for this color range.
69-
*/
70-
type?: FoldingRangeType;
71-
}
72-
73-
interface FoldingRangeRequest {
74-
/**
75-
* The text document.
76-
*/
77-
textDocument: TextDocumentIdentifier;
78-
}
79-
80-
namespace FoldingRangesRequest {
81-
export const type: RequestType<FoldingRangeRequest, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
82-
}
83-
8435
interface IPackageInfo {
8536
name: string;
8637
version: string;
@@ -106,6 +57,9 @@ interface JSONSchemaSettings {
10657

10758
let telemetryReporter: TelemetryReporter | undefined;
10859

60+
let foldingProviderRegistration: Disposable | undefined = void 0;
61+
const foldingSetting = 'json.experimental.syntaxFolding';
62+
10963
export function activate(context: ExtensionContext) {
11064

11165
let toDispose = context.subscriptions;
@@ -150,7 +104,7 @@ export function activate(context: ExtensionContext) {
150104
let disposable = client.start();
151105
toDispose.push(disposable);
152106
client.onReady().then(() => {
153-
client.onTelemetry(e => {
107+
disposable = client.onTelemetry(e => {
154108
if (telemetryReporter) {
155109
telemetryReporter.sendTelemetryEvent(e.key, e.data);
156110
}
@@ -176,16 +130,13 @@ export function activate(context: ExtensionContext) {
176130

177131
client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context));
178132

179-
languages.registerFoldingProvider(documentSelector, {
180-
provideFoldingRanges(document: TextDocument) {
181-
return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }).then(res => {
182-
if (res && Array.isArray(res.ranges)) {
183-
return new VSFoldingRangeList(res.ranges.map(r => new VSFoldingRange(r.startLine, r.endLine, r.type)));
184-
}
185-
return null;
186-
});
133+
initFoldingProvider();
134+
toDispose.push(workspace.onDidChangeConfiguration(c => {
135+
if (c.affectsConfiguration(foldingSetting)) {
136+
initFoldingProvider();
187137
}
188-
});
138+
}));
139+
toDispose.push({ dispose: () => foldingProviderRegistration && foldingProviderRegistration.dispose() });
189140
});
190141

191142
let languageConfiguration: LanguageConfiguration = {
@@ -197,6 +148,29 @@ export function activate(context: ExtensionContext) {
197148
};
198149
languages.setLanguageConfiguration('json', languageConfiguration);
199150
languages.setLanguageConfiguration('jsonc', languageConfiguration);
151+
152+
function initFoldingProvider() {
153+
let enable = workspace.getConfiguration().get(foldingSetting);
154+
if (enable) {
155+
if (!foldingProviderRegistration) {
156+
foldingProviderRegistration = languages.registerFoldingProvider(documentSelector, {
157+
provideFoldingRanges(document: TextDocument) {
158+
return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }).then(res => {
159+
if (res && Array.isArray(res.ranges)) {
160+
return new FoldingRangeList(res.ranges.map(r => new FoldingRange(r.startLine, r.endLine, r.type)));
161+
}
162+
return null;
163+
});
164+
}
165+
});
166+
}
167+
} else {
168+
if (foldingProviderRegistration) {
169+
foldingProviderRegistration.dispose();
170+
foldingProviderRegistration = void 0;
171+
}
172+
}
173+
}
200174
}
201175

202176
export function deactivate(): Promise<any> {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { TextDocumentIdentifier } from 'vscode-languageserver-types';
7+
import { RequestType, TextDocumentRegistrationOptions, StaticRegistrationOptions } from 'vscode-languageserver-protocol';
8+
9+
// ---- capabilities
10+
11+
export interface FoldingProviderClientCapabilities {
12+
/**
13+
* The text document client capabilities
14+
*/
15+
textDocument?: {
16+
/**
17+
* Capabilities specific to the foldingProvider
18+
*/
19+
foldingProvider?: {
20+
/**
21+
* Whether implementation supports dynamic registration. If this is set to `true`
22+
* the client supports the new `(FoldingProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
23+
* return value for the corresponding server capability as well.
24+
*/
25+
dynamicRegistration?: boolean;
26+
};
27+
};
28+
}
29+
30+
export interface FoldingProviderOptions {
31+
}
32+
33+
export interface FoldingProviderServerCapabilities {
34+
/**
35+
* The server provides folding provider support.
36+
*/
37+
foldingProvider?: FoldingProviderOptions | (FoldingProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions);
38+
}
39+
40+
export interface FoldingRangeList {
41+
/**
42+
* The folding ranges.
43+
*/
44+
ranges: FoldingRange[];
45+
}
46+
47+
export enum FoldingRangeType {
48+
/**
49+
* Folding range for a comment
50+
*/
51+
Comment = 'comment',
52+
/**
53+
* Folding range for a imports or includes
54+
*/
55+
Imports = 'imports',
56+
/**
57+
* Folding range for a region (e.g. `#region`)
58+
*/
59+
Region = 'region'
60+
}
61+
62+
export interface FoldingRange {
63+
64+
/**
65+
* The start line number
66+
*/
67+
startLine: number;
68+
69+
/**
70+
* The end line number
71+
*/
72+
endLine: number;
73+
74+
/**
75+
* The actual color value for this folding range.
76+
*/
77+
type?: FoldingRangeType | string;
78+
}
79+
80+
export interface FoldingRangeRequestParam {
81+
/**
82+
* The text document.
83+
*/
84+
textDocument: TextDocumentIdentifier;
85+
}
86+
87+
export namespace FoldingRangesRequest {
88+
export const type: RequestType<FoldingRangeRequestParam, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
89+
}

extensions/json/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@
152152
"default": true,
153153
"description": "%json.colorDecorators.enable.desc%",
154154
"deprecationMessage": "%json.colorDecorators.enable.deprecationMessage%"
155+
},
156+
"json.experimental.syntaxFolding": {
157+
"type": "boolean",
158+
"default": false,
159+
"description": "%json.experimental.syntaxFolding%"
155160
}
156161
}
157162
},

extensions/json/package.nls.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
"json.format.enable.desc": "Enable/disable default JSON formatter (requires restart)",
1010
"json.tracing.desc": "Traces the communication between VS Code and the JSON language server.",
1111
"json.colorDecorators.enable.desc": "Enables or disables color decorators",
12-
"json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`."
12+
"json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.",
13+
"json.experimental.syntaxFolding": "Enables/disables syntax aware folding markers."
1314
}

extensions/json/server/src/jsonServerMain.ts

Lines changed: 13 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import {
88
createConnection, IConnection,
99
TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType,
10-
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentIdentifier
10+
DocumentRangeFormattingRequest, Disposable, ServerCapabilities
1111
} from 'vscode-languageserver';
1212

1313
import { DocumentColorRequest, ServerCapabilities as CPServerCapabilities, ColorPresentationRequest } from 'vscode-languageserver-protocol/lib/protocol.colorProvider.proposed';
@@ -22,6 +22,8 @@ import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings,
2222
import { getLanguageModelCache } from './languageModelCache';
2323
import { createScanner, SyntaxKind } from 'jsonc-parser';
2424

25+
import { FoldingRangeType, FoldingRangesRequest, FoldingRange, FoldingRangeList, FoldingProviderServerCapabilities } from './protocol/foldingProvider.proposed';
26+
2527
interface ISchemaAssociations {
2628
[pattern: string]: string[];
2729
}
@@ -38,57 +40,6 @@ namespace SchemaContentChangeNotification {
3840
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
3941
}
4042

41-
interface FoldingRangeList {
42-
/**
43-
* The folding ranges.
44-
*/
45-
ranges: FoldingRange[];
46-
}
47-
48-
export enum FoldingRangeType {
49-
/**
50-
* Folding range for a comment
51-
*/
52-
Comment = 'comment',
53-
/**
54-
* Folding range for a imports or includes
55-
*/
56-
Imports = 'imports',
57-
/**
58-
* Folding range for a region (e.g. `#region`)
59-
*/
60-
Region = 'region'
61-
}
62-
63-
interface FoldingRange {
64-
65-
/**
66-
* The start line number
67-
*/
68-
startLine: number;
69-
70-
/**
71-
* The end line number
72-
*/
73-
endLine: number;
74-
75-
/**
76-
* The actual color value for this color range.
77-
*/
78-
type?: FoldingRangeType | string;
79-
}
80-
81-
interface FoldingRangeRequest {
82-
/**
83-
* The text document.
84-
*/
85-
textDocument: TextDocumentIdentifier;
86-
}
87-
88-
namespace FoldingRangesRequest {
89-
export const type: RequestType<FoldingRangeRequest, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
90-
}
91-
9243
// Create a connection for the server
9344
let connection: IConnection = createConnection();
9445

@@ -123,14 +74,15 @@ connection.onInitialize((params: InitializeParams): InitializeResult => {
12374

12475
clientSnippetSupport = hasClientCapability('textDocument', 'completion', 'completionItem', 'snippetSupport');
12576
clientDynamicRegisterSupport = hasClientCapability('workspace', 'symbol', 'dynamicRegistration');
126-
let capabilities: ServerCapabilities & CPServerCapabilities = {
77+
let capabilities: ServerCapabilities & CPServerCapabilities & FoldingProviderServerCapabilities = {
12778
// Tell the client that the server works in FULL text document sync mode
12879
textDocumentSync: documents.syncKind,
12980
completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : void 0,
13081
hoverProvider: true,
13182
documentSymbolProvider: true,
13283
documentRangeFormattingProvider: false,
133-
colorProvider: true
84+
colorProvider: true,
85+
foldingProvider: true
13486
};
13587

13688
return { capabilities };
@@ -418,8 +370,8 @@ connection.onRequest(FoldingRangesRequest.type, params => {
418370
let startLine = document.positionAt(scanner.getTokenOffset()).line;
419371
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
420372
stack.push(range);
421-
}
422373
break;
374+
}
423375
case SyntaxKind.CloseBraceToken:
424376
case SyntaxKind.CloseBracketToken: {
425377
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
@@ -432,17 +384,19 @@ connection.onRequest(FoldingRangesRequest.type, params => {
432384
prevStart = range.startLine;
433385
}
434386
}
435-
}
436387
break;
388+
}
389+
437390
case SyntaxKind.BlockCommentTrivia: {
438391
let startLine = document.positionAt(scanner.getTokenOffset()).line;
439392
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
440393
if (startLine < endLine) {
441394
ranges.push({ startLine, endLine, type: FoldingRangeType.Comment });
442395
prevStart = startLine;
443396
}
444-
}
445397
break;
398+
}
399+
446400
case SyntaxKind.LineCommentTrivia: {
447401
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
448402
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
@@ -467,8 +421,9 @@ connection.onRequest(FoldingRangesRequest.type, params => {
467421
}
468422
}
469423
}
470-
}
471424
break;
425+
}
426+
472427
}
473428
token = scanner.scan();
474429
}

0 commit comments

Comments
 (0)