Skip to content

Commit

Permalink
[json] setting for syntax folding (experimental)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeschli committed Feb 23, 2018
1 parent 71da90f commit 61a2adf
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 122 deletions.
100 changes: 37 additions & 63 deletions extensions/json/client/src/jsonMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import * as path from 'path';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();

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

import { FoldingRangesRequest } from './protocol/foldingProvider.proposed';

import { hash } from './utils/hash';

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

interface FoldingRangeList {
/**
* The folding ranges.
*/
ranges: FoldingRange[];
}

export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}

interface FoldingRange {

/**
* The start line number
*/
startLine: number;

/**
* The end line number
*/
endLine: number;

/**
* The actual color value for this color range.
*/
type?: FoldingRangeType;
}

interface FoldingRangeRequest {
/**
* The text document.
*/
textDocument: TextDocumentIdentifier;
}

namespace FoldingRangesRequest {
export const type: RequestType<FoldingRangeRequest, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
}

interface IPackageInfo {
name: string;
version: string;
Expand All @@ -106,6 +57,9 @@ interface JSONSchemaSettings {

let telemetryReporter: TelemetryReporter | undefined;

let foldingProviderRegistration: Disposable | undefined = void 0;
const foldingSetting = 'json.experimental.syntaxFolding';

export function activate(context: ExtensionContext) {

let toDispose = context.subscriptions;
Expand Down Expand Up @@ -150,7 +104,7 @@ export function activate(context: ExtensionContext) {
let disposable = client.start();
toDispose.push(disposable);
client.onReady().then(() => {
client.onTelemetry(e => {
disposable = client.onTelemetry(e => {
if (telemetryReporter) {
telemetryReporter.sendTelemetryEvent(e.key, e.data);
}
Expand All @@ -176,16 +130,13 @@ export function activate(context: ExtensionContext) {

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

languages.registerFoldingProvider(documentSelector, {
provideFoldingRanges(document: TextDocument) {
return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }).then(res => {
if (res && Array.isArray(res.ranges)) {
return new VSFoldingRangeList(res.ranges.map(r => new VSFoldingRange(r.startLine, r.endLine, r.type)));
}
return null;
});
initFoldingProvider();
toDispose.push(workspace.onDidChangeConfiguration(c => {
if (c.affectsConfiguration(foldingSetting)) {
initFoldingProvider();
}
});
}));
toDispose.push({ dispose: () => foldingProviderRegistration && foldingProviderRegistration.dispose() });
});

let languageConfiguration: LanguageConfiguration = {
Expand All @@ -197,6 +148,29 @@ export function activate(context: ExtensionContext) {
};
languages.setLanguageConfiguration('json', languageConfiguration);
languages.setLanguageConfiguration('jsonc', languageConfiguration);

function initFoldingProvider() {
let enable = workspace.getConfiguration().get(foldingSetting);
if (enable) {
if (!foldingProviderRegistration) {
foldingProviderRegistration = languages.registerFoldingProvider(documentSelector, {
provideFoldingRanges(document: TextDocument) {
return client.sendRequest(FoldingRangesRequest.type, { textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) }).then(res => {
if (res && Array.isArray(res.ranges)) {
return new FoldingRangeList(res.ranges.map(r => new FoldingRange(r.startLine, r.endLine, r.type)));
}
return null;
});
}
});
}
} else {
if (foldingProviderRegistration) {
foldingProviderRegistration.dispose();
foldingProviderRegistration = void 0;
}
}
}
}

export function deactivate(): Promise<any> {
Expand Down
89 changes: 89 additions & 0 deletions extensions/json/client/src/protocol/foldingProvider.proposed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { TextDocumentIdentifier } from 'vscode-languageserver-types';
import { RequestType, TextDocumentRegistrationOptions, StaticRegistrationOptions } from 'vscode-languageserver-protocol';

// ---- capabilities

export interface FoldingProviderClientCapabilities {
/**
* The text document client capabilities
*/
textDocument?: {
/**
* Capabilities specific to the foldingProvider
*/
foldingProvider?: {
/**
* Whether implementation supports dynamic registration. If this is set to `true`
* the client supports the new `(FoldingProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
* return value for the corresponding server capability as well.
*/
dynamicRegistration?: boolean;
};
};
}

export interface FoldingProviderOptions {
}

export interface FoldingProviderServerCapabilities {
/**
* The server provides folding provider support.
*/
foldingProvider?: FoldingProviderOptions | (FoldingProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions);
}

export interface FoldingRangeList {
/**
* The folding ranges.
*/
ranges: FoldingRange[];
}

export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}

export interface FoldingRange {

/**
* The start line number
*/
startLine: number;

/**
* The end line number
*/
endLine: number;

/**
* The actual color value for this folding range.
*/
type?: FoldingRangeType | string;
}

export interface FoldingRangeRequestParam {
/**
* The text document.
*/
textDocument: TextDocumentIdentifier;
}

export namespace FoldingRangesRequest {
export const type: RequestType<FoldingRangeRequestParam, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
}
5 changes: 5 additions & 0 deletions extensions/json/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@
"default": true,
"description": "%json.colorDecorators.enable.desc%",
"deprecationMessage": "%json.colorDecorators.enable.deprecationMessage%"
},
"json.experimental.syntaxFolding": {
"type": "boolean",
"default": false,
"description": "%json.experimental.syntaxFolding%"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion extensions/json/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"json.format.enable.desc": "Enable/disable default JSON formatter (requires restart)",
"json.tracing.desc": "Traces the communication between VS Code and the JSON language server.",
"json.colorDecorators.enable.desc": "Enables or disables color decorators",
"json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`."
"json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.",
"json.experimental.syntaxFolding": "Enables/disables syntax aware folding markers."
}
71 changes: 13 additions & 58 deletions extensions/json/server/src/jsonServerMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import {
createConnection, IConnection,
TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentIdentifier
DocumentRangeFormattingRequest, Disposable, ServerCapabilities
} from 'vscode-languageserver';

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

import { FoldingRangeType, FoldingRangesRequest, FoldingRange, FoldingRangeList, FoldingProviderServerCapabilities } from './protocol/foldingProvider.proposed';

interface ISchemaAssociations {
[pattern: string]: string[];
}
Expand All @@ -38,57 +40,6 @@ namespace SchemaContentChangeNotification {
export const type: NotificationType<string, any> = new NotificationType('json/schemaContent');
}

interface FoldingRangeList {
/**
* The folding ranges.
*/
ranges: FoldingRange[];
}

export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
}

interface FoldingRange {

/**
* The start line number
*/
startLine: number;

/**
* The end line number
*/
endLine: number;

/**
* The actual color value for this color range.
*/
type?: FoldingRangeType | string;
}

interface FoldingRangeRequest {
/**
* The text document.
*/
textDocument: TextDocumentIdentifier;
}

namespace FoldingRangesRequest {
export const type: RequestType<FoldingRangeRequest, FoldingRangeList | null, any, any> = new RequestType('textDocument/foldingRanges');
}

// Create a connection for the server
let connection: IConnection = createConnection();

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

clientSnippetSupport = hasClientCapability('textDocument', 'completion', 'completionItem', 'snippetSupport');
clientDynamicRegisterSupport = hasClientCapability('workspace', 'symbol', 'dynamicRegistration');
let capabilities: ServerCapabilities & CPServerCapabilities = {
let capabilities: ServerCapabilities & CPServerCapabilities & FoldingProviderServerCapabilities = {
// Tell the client that the server works in FULL text document sync mode
textDocumentSync: documents.syncKind,
completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : void 0,
hoverProvider: true,
documentSymbolProvider: true,
documentRangeFormattingProvider: false,
colorProvider: true
colorProvider: true,
foldingProvider: true
};

return { capabilities };
Expand Down Expand Up @@ -418,8 +370,8 @@ connection.onRequest(FoldingRangesRequest.type, params => {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
stack.push(range);
}
break;
}
case SyntaxKind.CloseBraceToken:
case SyntaxKind.CloseBracketToken: {
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
Expand All @@ -432,17 +384,19 @@ connection.onRequest(FoldingRangesRequest.type, params => {
prevStart = range.startLine;
}
}
}
break;
}

case SyntaxKind.BlockCommentTrivia: {
let startLine = document.positionAt(scanner.getTokenOffset()).line;
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (startLine < endLine) {
ranges.push({ startLine, endLine, type: FoldingRangeType.Comment });
prevStart = startLine;
}
}
break;
}

case SyntaxKind.LineCommentTrivia: {
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
Expand All @@ -467,8 +421,9 @@ connection.onRequest(FoldingRangesRequest.type, params => {
}
}
}
}
break;
}

}
token = scanner.scan();
}
Expand Down
Loading

0 comments on commit 61a2adf

Please sign in to comment.