diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index a6f6a0eb187..1d2fb5d827b 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -677,7 +677,7 @@ export interface LinkProvider { /** * A color inside the editor. */ -export interface IColor { +export interface IColorInfo { range: IRange; color: Color; } @@ -685,7 +685,7 @@ export interface IColor { * A provider of links. */ export interface ColorProvider { - provideColors(model: editorCommon.IReadOnlyModel, token: CancellationToken): IColor[] | Thenable; + provideColors(model: editorCommon.IReadOnlyModel, token: CancellationToken): IColorInfo[] | Thenable; } export interface IResourceEdit { diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPicker.ts b/src/vs/editor/contrib/colorPicker/browser/colorPicker.ts index 21159467569..a9feda23ad5 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPicker.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPicker.ts @@ -3,95 +3,134 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { editorAction, EditorAction, CommonEditorRegistry } from "vs/editor/common/editorCommonExtensions"; -import { KeyCode, KeyMod } from "vs/base/common/keyCodes"; -import { ServicesAccessor } from "vs/platform/instantiation/common/instantiation"; -import { EditorContextKeys } from "vs/editor/common/editorContextKeys"; -import * as nls from 'vs/nls'; -import { ICommonCodeEditor, IEditorContribution, IModelDeltaDecoration } from "vs/editor/common/editorCommon"; +import { ICommonCodeEditor, IEditorContribution } from "vs/editor/common/editorCommon"; import { editorContribution } from "vs/editor/browser/editorBrowserExtensions"; import { ICodeEditor } from "vs/editor/browser/editorBrowser"; -import { ColorPickerWidget } from "vs/editor/contrib/colorPicker/browser/colorPickerWidget"; -import { Disposable, empty as EmptyDisposable } from "vs/base/common/lifecycle"; +import { IDisposable } from "vs/base/common/lifecycle"; import { registerThemingParticipant } from "vs/platform/theme/common/themeService"; import { editorWidgetBackground, editorWidgetBorder } from "vs/platform/theme/common/colorRegistry"; -import { Color } from "vs/base/common/color"; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +// import { Color } from "vs/base/common/color"; +// import { ModelDecorationOptions } from 'vs/editor/common/model/textModelWithDecorations'; +import { ColorProviderRegistry, IColorInfo } from "vs/editor/common/modes"; +import { TPromise } from "vs/base/common/winjs.base"; +import { getColors } from "vs/editor/contrib/colorPicker/common/colorPicker"; +// import * as editorCommon from 'vs/editor/common/editorCommon'; @editorContribution -export class ColorPickerController extends Disposable implements IEditorContribution { +export class ColorPicker implements IEditorContribution { private static ID: string = 'editor.contrib.colorPicker'; - private widget: ColorPickerWidget; - // private model: ColorPickerModel; + private listenersToRemove: IDisposable[]; + private computePromise: TPromise; + + private currentDecorations: string[]; constructor(private editor: ICodeEditor) { - super(); + this.listenersToRemove = []; + + this.listenersToRemove.push(editor.onDidChangeModel((e) => this.onModelChanged())); } public getId(): string { - return ColorPickerController.ID; + return ColorPicker.ID; } - public static get(editor: ICommonCodeEditor): ColorPickerController { - return editor.getContribution(this.ID); + public static get(editor: ICommonCodeEditor): ColorPicker { + return editor.getContribution(this.ID); } public dispose(): void { - if (this.widget.visible) { - this.widget.dispose(); + } + + private onModelChanged(): void { + this.beginCompute(); + } + + private beginCompute(): void { + if (!this.editor.getModel()) { + return; } + + if (!ColorProviderRegistry.has(this.editor.getModel())) { + return; + } + + this.computePromise = getColors(this.editor.getModel()).then(colors => { + this.updateDecorations(colors); + this.computePromise = null; + }); + } + + private updateDecorations(colors: IColorInfo[]): void { + this.editor.changeDecorations((changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => { + let newDecorations: editorCommon.IModelDeltaDecoration[] = []; + for (let c of colors) { + newDecorations.push({ + range: { + startLineNumber: c.range.startLineNumber, + startColumn: c.range.startColumn, + endLineNumber: c.range.endLineNumber, + endColumn: c.range.endColumn + }, + options: { + color: c.color + } + }); + } + + this.currentDecorations = changeAccessor.deltaDecorations(this.currentDecorations, newDecorations); + }); } } -@editorContribution -export class FakeColorDecorations extends Disposable implements IEditorContribution { +// @editorContribution +// export class FakeColorDecorations extends Disposable implements IEditorContribution { - private static ID: string = 'editor.contrib.fakeColorDecorations'; +// private static ID: string = 'editor.contrib.fakeColorDecorations'; - private decorationsDisposable = EmptyDisposable; +// private decorationsDisposable = EmptyDisposable; - private static decorationOptions = ModelDecorationOptions.register({ - inlineClassName: 'detected-color', - color: Color.green - // hoverMessage: Color.green.toString() - }); +// private static decorationOptions = ModelDecorationOptions.register({ +// inlineClassName: 'detected-color', +// color: Color.green +// // hoverMessage: Color.green.toString() +// }); - constructor(private editor: ICodeEditor) { - super(); +// constructor(private editor: ICodeEditor) { +// super(); - this._register(editor.onDidChangeModel(e => { - this.decorationsDisposable.dispose(); +// this._register(editor.onDidChangeModel(e => { +// this.decorationsDisposable.dispose(); - const model = editor.getModel(); - const decoration: IModelDeltaDecoration = { - range: { - startLineNumber: 4, - startColumn: 1, - endLineNumber: 4, - endColumn: 10 - }, - options: FakeColorDecorations.decorationOptions - }; +// const model = editor.getModel(); +// const decoration: IModelDeltaDecoration = { +// range: { +// startLineNumber: 4, +// startColumn: 1, +// endLineNumber: 4, +// endColumn: 10 +// }, +// options: FakeColorDecorations.decorationOptions +// }; - const old = model.deltaDecorations([], [decoration]); +// const old = model.deltaDecorations([], [decoration]); - this.decorationsDisposable = { - dispose: () => { - model.deltaDecorations(old, []); - } - }; - })); - } +// this.decorationsDisposable = { +// dispose: () => { +// model.deltaDecorations(old, []); +// } +// }; +// })); +// } - public getId(): string { - return FakeColorDecorations.ID; - } +// public getId(): string { +// return FakeColorDecorations.ID; +// } - dispose(): void { - super.dispose(); - } -} +// dispose(): void { +// super.dispose(); +// } +// } registerThemingParticipant((theme, collector) => { const widgetBackground = theme.getColor(editorWidgetBackground); @@ -102,29 +141,29 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .colorpicker-header { border-bottom: 1px solid ${widgetBorder}; }`); }); -@editorAction -class ColorPickerCommand extends EditorAction { - constructor() { - super({ - id: 'editor.action.colorPicker', - label: nls.localize('editor.action.colorPicker', "Pick Color"), - alias: 'Pick Color', - precondition: null, - kbOpts: { - kbExpr: EditorContextKeys.textFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C - } - }); - } +// @editorAction +// class ColorPickerCommand extends EditorAction { +// constructor() { +// super({ +// id: 'editor.action.colorPicker', +// label: nls.localize('editor.action.colorPicker', "Pick Color"), +// alias: 'Pick Color', +// precondition: null, +// kbOpts: { +// kbExpr: EditorContextKeys.textFocus, +// primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C +// } +// }); +// } - public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { - let controller = ColorPickerController.get(editor); - if (!controller) { - return; - } +// public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void { +// let controller = ColorPickerController.get(editor); +// if (!controller) { +// return; +// } - controller.pickColor(); - } -} +// controller.pickColor(); +// } +// } -CommonEditorRegistry.registerEditorAction(new ColorPickerCommand()); \ No newline at end of file +// CommonEditorRegistry.registerEditorAction(new ColorPickerCommand()); \ No newline at end of file diff --git a/src/vs/editor/contrib/colorPicker/browser/elements/colorPickerBody.ts b/src/vs/editor/contrib/colorPicker/browser/elements/colorPickerBody.ts index a95ceb6df7f..e14cb7ca179 100644 --- a/src/vs/editor/contrib/colorPicker/browser/elements/colorPickerBody.ts +++ b/src/vs/editor/contrib/colorPicker/browser/elements/colorPickerBody.ts @@ -176,7 +176,7 @@ export class ColorPickerBody extends Disposable { private calculateHueRGB(slider: Slider): Color { const hueNormalizedHeight = this.hueStrip.offsetHeight - slider.domNode.offsetHeight; - const proportion = (hueNormalizedHeight - slider.top) / hueNormalizedHeight; + const proportion = 1 - ((hueNormalizedHeight - slider.top) / hueNormalizedHeight); const hue = proportion * 360; const hh = hue / 60; @@ -203,9 +203,9 @@ export class ColorPickerBody extends Disposable { b = X; } - r = Math.round(r * 255.0); - g = Math.round(g * 255.0); - b = Math.round(b * 255.0); + r = Math.round(r * 255); + g = Math.round(g * 255); + b = Math.round(b * 255); return Color.fromRGBA(new RGBA(r, g, b)); } diff --git a/src/vs/editor/contrib/colorPicker/common/colorPicker.ts b/src/vs/editor/contrib/colorPicker/common/colorPicker.ts new file mode 100644 index 00000000000..78e14decb17 --- /dev/null +++ b/src/vs/editor/contrib/colorPicker/common/colorPicker.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from "vs/base/common/winjs.base"; +import { Color } from "vs/base/common/color"; +import { ColorProviderRegistry, IColorInfo, ColorProvider } from "vs/editor/common/modes"; +import { asWinJsPromise } from "vs/base/common/async"; +import { onUnexpectedExternalError } from "vs/base/common/errors"; +import { IReadOnlyModel } from "vs/editor/common/editorCommon"; +import { IRange } from 'vs/editor/common/core/range'; + +export class ColorInfo implements IColorInfo { + + private _colorInfo: IColorInfo; + private _provider: ColorProvider; + + constructor(colorInfo: IColorInfo, provider: ColorProvider) { + this._colorInfo = colorInfo; + this._provider = provider; + } + + get range(): IRange { + return this._colorInfo.range; + } + + get color(): Color { + return this._colorInfo.color; + } +} + + +export function getColors(model: IReadOnlyModel): TPromise { + let colors: IColorInfo[] = []; + + // ask all providers for colors in parallel + const promises = ColorProviderRegistry.ordered(model).reverse().map(provider => { + return asWinJsPromise(token => provider.provideColors(model, token)).then(result => { + if (Array.isArray(result)) { + colors.concat(result); + } + }, onUnexpectedExternalError); + }); + + return TPromise.join(promises).then(() => { + return colors; + }); +} \ No newline at end of file diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6a1baf8a169..e7ec31d27d0 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -252,4 +252,41 @@ declare module 'vscode' { */ customRequest(command: string, args?: any): Thenable; } + + export enum ColorType { + RGBA = 0, + HSL = 1, + Hex = 2, + Custom = 3 + } + + export class Color { + r: number; + g: number; + b: number; + a: number; + } + + // TODO@Michel + export class ColorInfo { + /** + * The range this link applies to. + */ + range: Range; + + color: Color; + + type: ColorType; + + constructor(range: Range, color: Color); + } + + + export interface DocumentColorProvider { + provideDocumentColors(document: TextDocument, token: CancellationToken): ProviderResult; + } + + export namespace languages { + export function registerColorProvider(selector: DocumentSelector, provider: DocumentColorProvider): Disposable; + } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 03934ca9335..76b71e6b99c 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -265,6 +265,17 @@ export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape return undefined; } + // --- colors + + $registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): TPromise { + this._registrations[handle] = modes.ColorProviderRegistry.register(selector, { + provideColors: (model, token) => { + return wireCancellationToken(token, this._proxy.$provideDocumentColors(handle, model.uri)); + } + }); + return undefined; + } + // --- configuration $setLanguageConfiguration(handle: number, languageId: string, _configuration: vscode.LanguageConfiguration): TPromise { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 8e78a58bc46..46a556ceb6b 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -261,6 +261,9 @@ export function createApiFactory( registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { return languageFeatures.registerDocumentLinkProvider(selector, provider); }, + registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { + return languageFeatures.registerColorProvider(selector, provider); + }, setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { return languageFeatures.setLanguageConfiguration(language, configuration); } @@ -486,6 +489,10 @@ export function createApiFactory( // types CancellationTokenSource: CancellationTokenSource, CodeLens: extHostTypes.CodeLens, + Color: extHostTypes.Color, + ColorInfo: extHostTypes.ColorInfo, + ColorType: extHostTypes.ColorType, + EndOfLine: extHostTypes.EndOfLine, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, CompletionList: extHostTypes.CompletionList, @@ -495,7 +502,6 @@ export function createApiFactory( DocumentHighlight: extHostTypes.DocumentHighlight, DocumentHighlightKind: extHostTypes.DocumentHighlightKind, DocumentLink: extHostTypes.DocumentLink, - EndOfLine: extHostTypes.EndOfLine, EventEmitter: Emitter, Hover: extHostTypes.Hover, IndentAction: languageConfiguration.IndentAction, diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 31a81ca1809..e970480f9be 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -229,6 +229,7 @@ export abstract class MainThreadLanguageFeaturesShape { $registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[]): TPromise { throw ni(); } $registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise { throw ni(); } $registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } + $registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } $setLanguageConfiguration(handle: number, languageId: string, configuration: vscode.LanguageConfiguration): TPromise { throw ni(); } } @@ -474,6 +475,7 @@ export abstract class ExtHostLanguageFeaturesShape { $resolveCompletionItem(handle: number, resource: URI, position: IPosition, suggestion: modes.ISuggestion): TPromise { throw ni(); } $provideSignatureHelp(handle: number, resource: URI, position: IPosition): TPromise { throw ni(); } $provideDocumentLinks(handle: number, resource: URI): TPromise { throw ni(); } + $provideDocumentColors(handle: number, resource: URI): TPromise { throw ni(); } $resolveDocumentLink(handle: number, link: modes.ILink): TPromise { throw ni(); } } diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 2bf60e23f7a..87cab1f23c0 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -671,10 +671,32 @@ class LinkProviderAdapter { } } +class ColorProviderAdapter { + + private _documents: ExtHostDocuments; + private _provider: vscode.DocumentColorProvider; + + constructor(documents: ExtHostDocuments, provider: vscode.DocumentColorProvider) { + this._documents = documents; + this._provider = provider; + } + + provideColors(resource: URI): TPromise { + const doc = this._documents.getDocumentData(resource).document; + + return asWinJsPromise(token => this._provider.provideDocumentColors(doc, token)).then(colors => { + if (Array.isArray(colors)) { + return colors.map(TypeConverters.DocumentColor.from); + } + return undefined; + }); + } +} + type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter | DocumentHighlightAdapter | ReferenceAdapter | QuickFixAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter - | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter; + | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter; export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { @@ -959,6 +981,17 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link)); } + registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { + const handle = this._nextHandle(); + this._adapter.set(handle, new ColorProviderAdapter(this._documents, provider)); + this._proxy.$registerDocumentColorProvider(handle, selector); + return this._createDisposable(handle); + } + + $provideDocumentColors(handle: number, resource: URI): TPromise { + return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(resource)); + } + // --- configuration setLanguageConfiguration(languageId: string, configuration: vscode.LanguageConfiguration): vscode.Disposable { diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index afe975c891f..35a20446cb5 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -16,6 +16,7 @@ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; +import { RGBA, Color } from "vs/base/common/color"; export interface PositionLike { line: number; @@ -369,6 +370,15 @@ export namespace DocumentLink { } } +export namespace DocumentColor { + export function from(colorInfo: vscode.ColorInfo): modes.IColorInfo { + return { + range: fromRange(colorInfo.range), + color: Color.fromRGBA(new RGBA(colorInfo.color.r, colorInfo.color.g, colorInfo.color.b, colorInfo.color.a * 255)) + }; + } +} + export namespace TextDocumentSaveReason { export function to(reason: SaveReason): vscode.TextDocumentSaveReason { diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index dd6938bdab3..8ff5e83b5e0 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1015,6 +1015,39 @@ export class DocumentLink { } } +export enum ColorType { + RGBA = 0, + HSL = 1, + Hex = 2, + Custom = 3 +} + +export class Color { + r: number; + g: number; + b: number; + a: number; +} + +export class ColorInfo { + range: Range; + + color: Color; + + type: ColorType; + + constructor(range: Range, color: Color) { + if (color && !(color instanceof Color)) { + throw illegalArgument('target'); + } + if (!Range.isRange(range) || range.isEmpty) { + throw illegalArgument('range'); + } + this.range = range; + this.color = color; + } +} + export enum TaskRevealKind { Always = 1,