diff --git a/src/vs/editor/contrib/colorPicker/color.ts b/src/vs/editor/contrib/colorPicker/color.ts index d064e5e27f3f8e7b06d189b40a8aeacb9b5fc8b4..e3f8d6875100798d771fd11ff569b80fced522a8 100644 --- a/src/vs/editor/contrib/colorPicker/color.ts +++ b/src/vs/editor/contrib/colorPicker/color.ts @@ -3,10 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import URI from 'vs/base/common/uri'; + import { TPromise } from 'vs/base/common/winjs.base'; import { ColorProviderRegistry, DocumentColorProvider, IColorInformation, IColorPresentation } from 'vs/editor/common/modes'; import { asWinJsPromise } from 'vs/base/common/async'; import { ITextModel } from 'vs/editor/common/model'; +import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { Range, IRange } from 'vs/editor/common/core/range'; +import { illegalArgument } from 'vs/base/common/errors'; +import { IModelService } from 'vs/editor/common/services/modelService'; + export interface IColorData { colorInfo: IColorInformation; @@ -30,3 +37,57 @@ export function getColors(model: ITextModel): TPromise { export function getColorPresentations(model: ITextModel, colorInfo: IColorInformation, provider: DocumentColorProvider): TPromise { return asWinJsPromise(token => provider.provideColorPresentations(model, colorInfo, token)); } + +registerLanguageCommand('_executeDocumentColorProvider', function (accessor, args) { + + const { resource } = args; + if (!(resource instanceof URI)) { + throw illegalArgument(); + } + + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument(); + } + + const rawCIs: { range: IRange, color: [number, number, number, number] }[] = []; + const providers = ColorProviderRegistry.ordered(model).reverse(); + const promises = providers.map(provider => asWinJsPromise(token => provider.provideDocumentColors(model, token)).then(result => { + if (Array.isArray(result)) { + for (let ci of result) { + rawCIs.push({ range: ci.range, color: [ci.color.red, ci.color.green, ci.color.blue, ci.color.alpha] }); + } + } + })); + + return TPromise.join(promises).then(() => rawCIs); +}); + + +registerLanguageCommand('_executeColorPresentationProvider', function (accessor, args) { + + const { resource, color, range } = args; + if (!(resource instanceof URI) || !Array.isArray(color) || color.length !== 4 || !Range.isIRange(range)) { + throw illegalArgument(); + } + const [red, green, blue, alpha] = color; + + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument(); + } + + const colorInfo = { + range, + color: { red, green, blue, alpha } + }; + + const presentations: IColorPresentation[] = []; + const providers = ColorProviderRegistry.ordered(model).reverse(); + const promises = providers.map(provider => asWinJsPromise(token => provider.provideColorPresentations(model, colorInfo, token)).then(result => { + if (Array.isArray(result)) { + presentations.push(...result); + } + })); + return TPromise.join(promises).then(() => presentations); +}); diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 3122072f43c1730d396e8cac0851b9ea250bedc4..b9d2e6ba55d49b8cb69bcf701480311fb4cb9860 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -10,6 +10,8 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import * as vscode from 'vscode'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import * as types from 'vs/workbench/api/node/extHostTypes'; +import { IRawColorInfo } from 'vs/workbench/api/node/extHost.protocol'; + import { ISingleEditOperation } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; @@ -177,7 +179,21 @@ export class ExtHostApiCommands { args: [], returns: 'An array of task handles' }); - + this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, { + description: 'Execute document color provider.', + args: [ + { name: 'uri', description: 'Uri of a text document', constraint: URI }, + ], + returns: 'A promise that resolves to an array of ColorInformation objects.' + }); + this._register('vscode.executeColorPresentationProvider', this._executeColorPresentationProvider, { + description: 'Execute color presentation provider.', + args: [ + { name: 'color', description: 'The color to show and insert', constraint: types.Color }, + { name: 'context', description: 'Context object with uri and range' } + ], + returns: 'A promise that resolves to an array of ColorPresentation objects.' + }); this._register('vscode.previewHtml', (uri: URI, position?: vscode.ViewColumn, label?: string, options?: any) => { return this._commands.executeCommand('_workbench.previewHtml', uri, @@ -391,6 +407,32 @@ export class ExtHostApiCommands { }); } + private _executeDocumentColorProvider(resource: URI): Thenable { + const args = { + resource + }; + return this._commands.executeCommand('_executeDocumentColorProvider', args).then(result => { + if (result) { + return result.map(ci => ({ range: typeConverters.toRange(ci.range), color: typeConverters.Color.to(ci.color) })); + } + return []; + }); + } + + private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range }): Thenable { + const args = { + resource: context.uri, + color: typeConverters.Color.from(color), + range: typeConverters.fromRange(context.range), + }; + return this._commands.executeCommand('_executeColorPresentationProvider', args).then(result => { + if (result) { + return result.map(typeConverters.ColorPresentation.to); + } + return []; + }); + } + private _executeDocumentSymbolProvider(resource: URI): Thenable { const args = { resource diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index cb69eb21452cb918f29830d68f437ec3509eeb20..55d6a6e3d8a9f7d837d184b7fe523bc9b0df930b 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { mixin } from 'vs/base/common/objects'; import * as vscode from 'vscode'; import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters'; -import { Range, Disposable, CompletionList, SnippetString, Color, CodeActionKind } from 'vs/workbench/api/node/extHostTypes'; +import { Range, Disposable, CompletionList, SnippetString, CodeActionKind } from 'vs/workbench/api/node/extHostTypes'; import { ISingleEditOperation } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; @@ -780,7 +780,7 @@ class ColorProviderAdapter { const colorInfos: IRawColorInfo[] = colors.map(ci => { return { - color: [ci.color.red, ci.color.green, ci.color.blue, ci.color.alpha] as [number, number, number, number], + color: TypeConverters.Color.from(ci.color), range: TypeConverters.fromRange(ci.range) }; }); @@ -792,7 +792,7 @@ class ColorProviderAdapter { provideColorPresentations(resource: URI, raw: IRawColorInfo): TPromise { const document = this._documents.getDocumentData(resource).document; const range = TypeConverters.toRange(raw.range); - const color = new Color(raw.color[0], raw.color[1], raw.color[2], raw.color[3]); + const color = TypeConverters.Color.to(raw.color); return asWinJsPromise(token => this._provider.provideColorPresentations(color, { document, range }, token)).then(value => { return value.map(TypeConverters.ColorPresentation.from); }); diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index bf5bf34612d888ebd9516222ee0bdf470f499bf5..0f4121566744f80fd3b1e320adcd5e0ecca051c6 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -83,7 +83,6 @@ export function fromPosition(position: types.Position): IPosition { return { lineNumber: position.line + 1, column: position.character + 1 }; } - export function fromDiagnostic(value: vscode.Diagnostic): IMarkerData { return { ...fromRange(value.range), @@ -245,7 +244,7 @@ export const TextEdit = { range: fromRange(edit.range) }; }, - to(edit: modes.TextEdit): vscode.TextEdit { + to(edit: modes.TextEdit): types.TextEdit { let result = new types.TextEdit(toRange(edit.range), edit.text); result.newEol = EndOfLine.to(edit.eol); return result; @@ -547,12 +546,15 @@ export namespace DocumentLink { } export namespace ColorPresentation { - export function to(colorPresentation: modes.IColorPresentation): vscode.ColorPresentation { - return { - label: colorPresentation.label, - textEdit: colorPresentation.textEdit ? TextEdit.to(colorPresentation.textEdit) : undefined, - additionalTextEdits: colorPresentation.additionalTextEdits ? colorPresentation.additionalTextEdits.map(value => TextEdit.to(value)) : undefined - }; + export function to(colorPresentation: modes.IColorPresentation): types.ColorPresentation { + let cp = new types.ColorPresentation(colorPresentation.label); + if (colorPresentation.textEdit) { + cp.textEdit = TextEdit.to(colorPresentation.textEdit); + } + if (colorPresentation.additionalTextEdits) { + cp.additionalTextEdits = colorPresentation.additionalTextEdits.map(value => TextEdit.to(value)); + } + return cp; } export function from(colorPresentation: vscode.ColorPresentation): modes.IColorPresentation { @@ -564,6 +566,15 @@ export namespace ColorPresentation { } } +export namespace Color { + export function to(c: [number, number, number, number]): types.Color { + return new types.Color(c[0], c[1], c[2], c[3]); + } + export function from(color: types.Color): [number, number, number, number] { + return [color.red, color.green, color.blue, color.alpha]; + } +} + export namespace TextDocumentSaveReason { export function to(reason: SaveReason): vscode.TextDocumentSaveReason { diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 5e6b7013ec1e0d490b4c6a29c0a3f143044f90d2..3ddac7d1918ff1b3d77fe36e4ee687104ace6ee9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -522,4 +522,55 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); }); + + + test('Color provider', function () { + + disposables.push(extHost.registerColorProvider(defaultSelector, { + provideDocumentColors(): vscode.ColorInformation[] { + return [new types.ColorInformation(new types.Range(0, 0, 0, 20), new types.Color(0.1, 0.2, 0.3, 0.4))]; + }, + provideColorPresentations(color: vscode.Color, context: { range: vscode.Range, document: vscode.TextDocument }): vscode.ColorPresentation[] { + const cp = new types.ColorPresentation('#ABC'); + cp.textEdit = types.TextEdit.replace(new types.Range(1, 0, 1, 20), '#ABC'); + cp.additionalTextEdits = [types.TextEdit.insert(new types.Position(2, 20), '*')]; + return [cp]; + } + })); + + return rpcProtocol.sync().then(() => { + return commands.executeCommand('vscode.executeDocumentColorProvider', model.uri).then(value => { + assert.equal(value.length, 1); + let [first] = value; + + assert.equal(first.color.red, 0.1); + assert.equal(first.color.green, 0.2); + assert.equal(first.color.blue, 0.3); + assert.equal(first.color.alpha, 0.4); + assert.equal(first.range.start.line, 0); + assert.equal(first.range.start.character, 0); + assert.equal(first.range.end.line, 0); + assert.equal(first.range.end.character, 20); + }); + }).then(() => { + const color = new types.Color(0.5, 0.6, 0.7, 0.8); + const range = new types.Range(0, 0, 0, 20); + return commands.executeCommand('vscode.executeColorPresentationProvider', color, { uri: model.uri, range }).then(value => { + assert.equal(value.length, 1); + let [first] = value; + + assert.equal(first.label, '#ABC'); + assert.equal(first.textEdit.newText, '#ABC'); + assert.equal(first.textEdit.range.start.line, 1); + assert.equal(first.textEdit.range.start.character, 0); + assert.equal(first.textEdit.range.end.line, 1); + assert.equal(first.textEdit.range.end.character, 20); + assert.equal(first.additionalTextEdits.length, 1); + assert.equal(first.additionalTextEdits[0].range.start.line, 2); + assert.equal(first.additionalTextEdits[0].range.start.character, 20); + assert.equal(first.additionalTextEdits[0].range.end.line, 2); + assert.equal(first.additionalTextEdits[0].range.end.character, 20); + }); + }); + }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index ad73ec6a899c0be763b90cd66a05f8e1fdfe7e73..daeba76fd089a34b002508c94c9d4614aa207eb2 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -45,6 +45,7 @@ import * as vscode from 'vscode'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITextModel, EndOfLineSequence } from 'vs/editor/common/model'; +import { getColors } from 'vs/editor/contrib/colorPicker/color'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -1166,4 +1167,26 @@ suite('ExtHostLanguageFeatures', function () { }); }); }); + + test('Document colors, data conversion', function () { + + disposables.push(extHost.registerColorProvider(defaultSelector, { + provideDocumentColors(): vscode.ColorInformation[] { + return [new types.ColorInformation(new types.Range(0, 0, 0, 20), new types.Color(0.1, 0.2, 0.3, 0.4))]; + }, + provideColorPresentations(color: vscode.Color, context: { range: vscode.Range, document: vscode.TextDocument }): vscode.ColorPresentation[] { + return []; + } + })); + + return rpcProtocol.sync().then(() => { + return getColors(model).then(value => { + assert.equal(value.length, 1); + let [first] = value; + + assert.deepEqual(first.colorInfo.color, { red: 0.1, green: 0.2, blue: 0.3, alpha: 0.4 }); + assert.deepEqual(first.colorInfo.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 21 }); + }); + }); + }); });