From abc999376649a4388a21bd1907924e832f1dafee Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 17 Oct 2016 14:38:08 +0200 Subject: [PATCH 1/5] add [typescript|javascript].formate.enable --- extensions/typescript/package.json | 27 +++++++++++++++---- extensions/typescript/package.nls.json | 4 ++- .../src/features/formattingProvider.ts | 6 +++++ extensions/typescript/src/typescriptMain.ts | 14 ++++++++-- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/extensions/typescript/package.json b/extensions/typescript/package.json index 07eec17eb85..baf7c87a425 100644 --- a/extensions/typescript/package.json +++ b/extensions/typescript/package.json @@ -6,7 +6,7 @@ "author": "Microsoft Corporation", "license": "MIT", "publisher": "vscode", - "aiKey":"AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { "vscode": "*" }, @@ -74,7 +74,10 @@ "order": 20, "properties": { "typescript.tsdk": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null, "description": "%typescript.tsdk.desc%" }, @@ -83,19 +86,23 @@ "default": false, "description": "%typescript.experimentalAutomaticTypeAcquisition%" }, - "typescript.check.workspaceVersion" :{ + "typescript.check.workspaceVersion": { "type": "boolean", "default": true, "description": "%typescript.check.workspaceVersion%" }, - "typescript.check.tscVersion" :{ + "typescript.check.tscVersion": { "type": "boolean", "default": true, "description": "%typescript.check.tscVersion%" }, "typescript.tsserver.trace": { "type": "string", - "enum": ["off", "messages", "verbose"], + "enum": [ + "off", + "messages", + "verbose" + ], "default": "off", "description": "%typescript.tsserver.trace%" }, @@ -109,6 +116,11 @@ "default": true, "description": "%typescript.validate.enable%" }, + "typescript.format.enable": { + "type": "boolean", + "default": true, + "description": "%typescript.format.enable%" + }, "typescript.format.insertSpaceAfterCommaDelimiter": { "type": "boolean", "default": true, @@ -169,6 +181,11 @@ "default": true, "description": "%javascript.validate.enable%" }, + "javascript.format.enable": { + "type": "boolean", + "default": true, + "description": "%javascript.format.enable%" + }, "javascript.format.insertSpaceAfterCommaDelimiter": { "type": "boolean", "default": true, diff --git a/extensions/typescript/package.nls.json b/extensions/typescript/package.nls.json index 2fe9a42de86..65efbeb5f13 100644 --- a/extensions/typescript/package.nls.json +++ b/extensions/typescript/package.nls.json @@ -10,7 +10,9 @@ "typescript.check.tscVersion": "Check if a global install TypeScript compiler (e.g. tsc) differs from the used TypeScript language service.", "typescript.tsserver.trace": "Enables tracing of messages send to the TS server", "typescript.tsserver.experimentalAutoBuild": "Enables experimental auto build. Requires 1.9 dev or 2.x tsserver version and a restart of VS Code after changing it.", - "typescript.validate.enable": "Enable / disable TypeScript validation", + "typescript.validate.enable": "Enable/disable TypeScript validation", + "typescript.format.enable": "Enable/disable default TypeScript formatter", + "javascript.format.enable": "Enable/disable default JavaScript formatter", "format.insertSpaceAfterCommaDelimiter": "Defines space handling after a comma delimiter", "format.insertSpaceAfterSemicolonInForStatements": " Defines space handling after a semicolon in a for statement", "format.insertSpaceBeforeAndAfterBinaryOperators": "Defines space handling after a binary operator", diff --git a/extensions/typescript/src/features/formattingProvider.ts b/extensions/typescript/src/features/formattingProvider.ts index 92a5424c7aa..f3ab6c542f5 100644 --- a/extensions/typescript/src/features/formattingProvider.ts +++ b/extensions/typescript/src/features/formattingProvider.ts @@ -11,6 +11,7 @@ import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; interface Configuration { + enable: boolean; insertSpaceAfterCommaDelimiter: boolean; insertSpaceAfterSemicolonInForStatements: boolean; insertSpaceBeforeAndAfterBinaryOperators: boolean; @@ -50,6 +51,7 @@ namespace Configuration { export function def(): Configuration { let result: Configuration = Object.create(null); + result.enable = true; result.insertSpaceAfterCommaDelimiter = true; result.insertSpaceAfterSemicolonInForStatements = true; result.insertSpaceBeforeAndAfterBinaryOperators = true; @@ -86,6 +88,10 @@ export default class TypeScriptFormattingProvider implements DocumentRangeFormat } } + public isEnabled(): boolean { + return this.config.enable; + } + private ensureFormatOptions(document: TextDocument, options: FormattingOptions, token: CancellationToken): Promise { let key = document.uri.toString(); let currentOptions = this.formatOptions[key]; diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts index 81d257b45b9..4b6a7e381ed 100644 --- a/extensions/typescript/src/typescriptMain.ts +++ b/extensions/typescript/src/typescriptMain.ts @@ -9,7 +9,7 @@ * ------------------------------------------------------------------------------------------ */ 'use strict'; -import { env, languages, commands, workspace, window, Uri, ExtensionContext, Memento, IndentAction, Diagnostic, DiagnosticCollection, Range, DocumentFilter } from 'vscode'; +import { env, languages, commands, workspace, window, Uri, ExtensionContext, Memento, IndentAction, Diagnostic, DiagnosticCollection, Range, DocumentFilter, Disposable } from 'vscode'; // This must be the first statement otherwise modules might got loaded with // the wrong locale. @@ -103,6 +103,7 @@ class LanguageProvider { private completionItemProvider: CompletionItemProvider; private formattingProvider: FormattingProvider; + private formattingProviderRegistration: Disposable; private _validate: boolean; @@ -147,6 +148,9 @@ class LanguageProvider { let renameProvider = new RenameProvider(client); this.formattingProvider = new FormattingProvider(client); this.formattingProvider.updateConfiguration(config); + if (this.formattingProvider.isEnabled) { + this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider); + } this.description.modeIds.forEach(modeId => { let selector: DocumentFilter = { scheme: 'file', language: modeId }; @@ -158,7 +162,6 @@ class LanguageProvider { languages.registerDocumentSymbolProvider(selector, documentSymbolProvider); languages.registerSignatureHelpProvider(selector, signatureHelpProvider, '(', ','); languages.registerRenameProvider(selector, renameProvider); - languages.registerDocumentRangeFormattingEditProvider(selector, this.formattingProvider); languages.registerOnTypeFormattingEditProvider(selector, this.formattingProvider, ';', '}', '\n'); languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(client, modeId)); languages.setLanguageConfiguration(modeId, { @@ -209,6 +212,13 @@ class LanguageProvider { } if (this.formattingProvider) { this.formattingProvider.updateConfiguration(config); + if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) { + this.formattingProviderRegistration.dispose(); + this.formattingProviderRegistration = undefined; + + } else if (this.formattingProvider.isEnabled() && !this.formattingProviderRegistration) { + this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider); + } } } From d5ccd35d0fb807974f6e5167ef373474add92445 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 17 Oct 2016 14:50:56 +0200 Subject: [PATCH 2/5] fallback to next formatter when the previous didn't produce a result --- src/vs/editor/contrib/format/common/format.ts | 42 ++++++++++++++----- .../node/api/extHostLanguageFeatures.test.ts | 27 +++++++++++- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/format/common/format.ts b/src/vs/editor/contrib/format/common/format.ts index 92a21dbfb50..22f4be617f1 100644 --- a/src/vs/editor/contrib/format/common/format.ts +++ b/src/vs/editor/contrib/format/common/format.ts @@ -7,38 +7,58 @@ import { illegalArgument } from 'vs/base/common/errors'; import URI from 'vs/base/common/uri'; +import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { TPromise } from 'vs/base/common/winjs.base'; import { Range } from 'vs/editor/common/core/range'; import { IReadOnlyModel, ISingleEditOperation } from 'vs/editor/common/editorCommon'; import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { asWinJsPromise } from 'vs/base/common/async'; +import { asWinJsPromise, sequence } from 'vs/base/common/async'; import { Position } from 'vs/editor/common/core/position'; export function getDocumentRangeFormattingEdits(model: IReadOnlyModel, range: Range, options: FormattingOptions): TPromise { - const [support] = DocumentRangeFormattingEditProviderRegistry.ordered(model); - if (!support) { + const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model); + + if (providers.length === 0) { return TPromise.as(undefined); } - return asWinJsPromise((token) => { - return support.provideDocumentRangeFormattingEdits(model, range, options, token); - }); + let result: ISingleEditOperation[]; + return sequence(providers.map(provider => { + if (isFalsyOrEmpty(result)) { + return () => { + return asWinJsPromise(token => provider.provideDocumentRangeFormattingEdits(model, range, options, token)).then(value => { + result = value; + }, err => { + // ignore + }); + }; + } + })).then(() => result); } export function getDocumentFormattingEdits(model: IReadOnlyModel, options: FormattingOptions): TPromise { - const [support] = DocumentFormattingEditProviderRegistry.ordered(model); + const providers = DocumentFormattingEditProviderRegistry.ordered(model); // try range formatters when no document formatter is registered - if (!support) { + if (providers.length === 0) { return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options); } - return asWinJsPromise((token) => { - return support.provideDocumentFormattingEdits(model, options, token); - }); + let result: ISingleEditOperation[]; + return sequence(providers.map(provider => { + if (isFalsyOrEmpty(result)) { + return () => { + return asWinJsPromise(token => provider.provideDocumentFormattingEdits(model, options, token)).then(value => { + result = value; + }, err => { + // ignore + }); + }; + } + })).then(() => result); } export function getOnTypeFormattingEdits(model: IReadOnlyModel, position: Position, ch: string, options: FormattingOptions): TPromise { diff --git a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts index a54097ee8e7..b22f37e2e6f 100644 --- a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts @@ -884,7 +884,30 @@ suite('ExtHostLanguageFeatures', function () { })); return threadService.sync().then(() => { - return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }).then(_ => { throw new Error(); }, err => { }); + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }); + }); + }); + + test('Format Doc, order', function () { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + provideDocumentFormattingEdits(): any { + return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')]; + } + })); + + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + provideDocumentFormattingEdits(): any { + return undefined; + } + })); + + return threadService.sync().then(() => { + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }).then(value => { + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.text, 'testing'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + }); }); }); @@ -933,7 +956,7 @@ suite('ExtHostLanguageFeatures', function () { })); return threadService.sync().then(() => { - return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }).then(_ => { throw new Error(); }, err => { }); + return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }); }); }); From 357ff62f682b4fdca4358edaf86c0f71df5803a8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 14 Oct 2016 10:05:25 +0200 Subject: [PATCH 3/5] add comment about document and document range provider --- src/vs/vscode.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 1ddfc16d4a5..1afbaa44ab1 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4074,6 +4074,10 @@ declare namespace vscode { /** * Register a formatting provider for a document range. * + * *Note:* A document range provider is also a [document formatter](#DocumentFormattingEditProvider) + * which means there is no need to [register](registerDocumentFormattingEditProvider) a document + * formatter when also registering a range provider. + * * Multiple providers can be registered for a language. In that case providers are sorted * by their [score](#languages.match) and the best-matching provider is used. Failure * of the selected provider will cause a failure of the whole operation. From db48473d519788a645407c304be626edf1ef8c9a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 17 Oct 2016 15:25:35 +0200 Subject: [PATCH 4/5] json.format.enable --- extensions/json/client/src/jsonMain.ts | 3 ++- extensions/json/package.json | 7 ++++++- extensions/json/server/src/jsonServerMain.ts | 8 +------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/json/client/src/jsonMain.ts b/extensions/json/client/src/jsonMain.ts index 10ef18a39dc..f9757ad16ac 100644 --- a/extensions/json/client/src/jsonMain.ts +++ b/extensions/json/client/src/jsonMain.ts @@ -61,7 +61,8 @@ export function activate(context: ExtensionContext) { fileEvents: workspace.createFileSystemWatcher('**/*.json') }, initializationOptions: { - languageIds + languageIds, + ['format.enable']: workspace.getConfiguration('json').get('format.enable') } }; diff --git a/extensions/json/package.json b/extensions/json/package.json index b5603841c6b..e8536b44507 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -92,6 +92,11 @@ } } } + }, + "json.format.enable": { + "type": "boolean", + "default": true, + "description": "Enable/disable default JSON formatter" } } } @@ -101,4 +106,4 @@ "vscode-languageclient": "^2.4.2-next.22", "vscode-nls": "^1.0.7" } -} +} \ No newline at end of file diff --git a/extensions/json/server/src/jsonServerMain.ts b/extensions/json/server/src/jsonServerMain.ts index 93f510d8770..b0afbb79f5d 100644 --- a/extensions/json/server/src/jsonServerMain.ts +++ b/extensions/json/server/src/jsonServerMain.ts @@ -64,8 +64,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { completionProvider: { resolveProvider: true, triggerCharacters: ['"', ':'] }, hoverProvider: true, documentSymbolProvider: true, - documentRangeFormattingProvider: true, - documentFormattingProvider: true + documentRangeFormattingProvider: params.initializationOptions['format.enable'] } }; }); @@ -278,11 +277,6 @@ connection.onDocumentSymbol(documentSymbolParams => { return languageService.findDocumentSymbols(document, jsonDocument); }); -connection.onDocumentFormatting(formatParams => { - let document = documents.get(formatParams.textDocument.uri); - return languageService.format(document, null, formatParams.options); -}); - connection.onDocumentRangeFormatting(formatParams => { let document = documents.get(formatParams.textDocument.uri); return languageService.format(document, formatParams.range, formatParams.options); From 2d9d187f5bf2522aa2b4af99082cd89ab578cac6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 17 Oct 2016 15:30:11 +0200 Subject: [PATCH 5/5] html.format.enable --- extensions/html/client/src/htmlMain.ts | 3 +- extensions/html/package.json | 59 +++++++++++++++----- extensions/html/server/src/htmlServerMain.ts | 8 +-- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/extensions/html/client/src/htmlMain.ts b/extensions/html/client/src/htmlMain.ts index 8b3dcf33e4f..dbca27b6bf3 100644 --- a/extensions/html/client/src/htmlMain.ts +++ b/extensions/html/client/src/htmlMain.ts @@ -6,7 +6,7 @@ import * as path from 'path'; -import { languages, ExtensionContext, IndentAction } from 'vscode'; +import { languages, workspace, ExtensionContext, IndentAction } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient'; import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; @@ -36,6 +36,7 @@ export function activate(context: ExtensionContext) { configurationSection: ['html'], }, initializationOptions: { + ['format.enable']: workspace.getConfiguration('html').get('format.enable') } }; diff --git a/extensions/html/package.json b/extensions/html/package.json index 468878ad63d..c8b7c47a9ff 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -18,25 +18,56 @@ "install-client-next": "npm install vscode-languageclient@next -f -S", "install-client-local": "npm install ../../../vscode-languageserver-node/client -f -S" }, - "contributes": { - "languages": [{ - "id": "html", - "extensions": [ ".html", ".htm", ".shtml", ".xhtml", ".mdoc", ".jsp", ".asp", ".aspx", ".jshtm", ".vue" ], - "aliases": [ "HTML", "htm", "html", "xhtml" ], - "mimetypes": ["text/html", "text/x-jshtm", "text/template", "text/ng-template", "application/xhtml+xml"], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "html", - "scopeName": "text.html.basic", - "path": "./syntaxes/html.json" - }], + "contributes": { + "languages": [ + { + "id": "html", + "extensions": [ + ".html", + ".htm", + ".shtml", + ".xhtml", + ".mdoc", + ".jsp", + ".asp", + ".aspx", + ".jshtm", + ".vue" + ], + "aliases": [ + "HTML", + "htm", + "html", + "xhtml" + ], + "mimetypes": [ + "text/html", + "text/x-jshtm", + "text/template", + "text/ng-template", + "application/xhtml+xml" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "html", + "scopeName": "text.html.basic", + "path": "./syntaxes/html.json" + } + ], "configuration": { "id": "html", "order": 20, "type": "object", "title": "HTML", "properties": { + "html.format.enable": { + "type": "boolean", + "default": true, + "description": "Enable/disable default HTML formatter" + }, "html.format.wrapLineLength": { "type": "integer", "default": 120, @@ -108,4 +139,4 @@ "vscode-languageclient": "^2.6.0-next.1", "vscode-nls": "^1.0.7" } -} +} \ No newline at end of file diff --git a/extensions/html/server/src/htmlServerMain.ts b/extensions/html/server/src/htmlServerMain.ts index 557bbfa1c57..93b73b6929f 100644 --- a/extensions/html/server/src/htmlServerMain.ts +++ b/extensions/html/server/src/htmlServerMain.ts @@ -48,8 +48,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { textDocumentSync: documents.syncKind, completionProvider: { resolveProvider: false, triggerCharacters: ['.', ':', '<', '"', '=', '/'] }, documentHighlightProvider: true, - documentRangeFormattingProvider: true, - documentFormattingProvider: true, + documentRangeFormattingProvider: params.initializationOptions['format.enable'], documentLinkProvider: true } }; @@ -106,11 +105,6 @@ function getFormattingOptions(formatParams: FormattingOptions) { return merge(formatParams, merge(formatSettings, {})); } -connection.onDocumentFormatting(formatParams => { - let document = documents.get(formatParams.textDocument.uri); - return languageService.format(document, null, getFormattingOptions(formatParams.options)); -}); - connection.onDocumentRangeFormatting(formatParams => { let document = documents.get(formatParams.textDocument.uri); return languageService.format(document, formatParams.range, getFormattingOptions(formatParams.options));