33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6+ import { RGBA } from 'vs/base/common/color' ;
7+ import { hash } from 'vs/base/common/hash' ;
8+ import { IDisposable , dispose } from 'vs/base/common/lifecycle' ;
9+ import { TPromise } from 'vs/base/common/winjs.base' ;
610import { ICommonCodeEditor , IEditorContribution } from 'vs/editor/common/editorCommon' ;
711import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions' ;
812import { ICodeEditor } from 'vs/editor/browser/editorBrowser' ;
9- import { IDisposable , dispose } from 'vs/base/common/lifecycle' ;
10- import { ColorProviderRegistry , IColorRange } from 'vs/editor/common/modes' ;
11- import { TPromise } from 'vs/base/common/winjs.base' ;
12- import { getColors } from 'vs/editor/contrib/colorPicker/common/color' ;
1313import { Range } from 'vs/editor/common/core/range' ;
1414import { Position } from 'vs/editor/common/core/position' ;
15+ import { ColorProviderRegistry , IColorRange } from 'vs/editor/common/modes' ;
16+ import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService' ;
17+ import { getColors } from 'vs/editor/contrib/colorPicker/common/color' ;
18+
19+ const MAX_DECORATORS = 500 ;
1520
1621@editorContribution
1722export class ColorDetector implements IEditorContribution {
@@ -20,22 +25,27 @@ export class ColorDetector implements IEditorContribution {
2025
2126 static RECOMPUTE_TIME = 1000 ; // ms
2227
23- private listenersToRemove : IDisposable [ ] = [ ] ;
28+ private globalToDispose : IDisposable [ ] = [ ] ;
29+ private localToDispose : IDisposable [ ] = [ ] ;
2430 private computePromise : TPromise < void > ;
2531 private timeoutPromise : TPromise < void > ;
2632
2733 private decorationsIds : string [ ] = [ ] ;
2834 private colorRanges = new Map < string , IColorRange > ( ) ;
2935
30- constructor ( private editor : ICodeEditor ) {
31- this . listenersToRemove . push ( editor . onDidChangeModelContent ( ( e ) => this . onChange ( ) ) ) ;
32- this . listenersToRemove . push ( editor . onDidChangeModel ( ( e ) => this . onModelChanged ( ) ) ) ;
33- this . listenersToRemove . push ( editor . onDidChangeModelLanguage ( ( e ) => this . onModelModeChanged ( ) ) ) ;
34- this . listenersToRemove . push ( ColorProviderRegistry . onDidChange ( ( e ) => this . onModelModeChanged ( ) ) ) ;
36+ private _colorDecorators : string [ ] = [ ] ;
37+ private _decorationsTypes : { [ key : string ] : boolean } = { } ;
38+
39+ constructor ( private editor : ICodeEditor ,
40+ @ICodeEditorService private _codeEditorService : ICodeEditorService ,
41+ ) {
42+ this . globalToDispose . push ( editor . onDidChangeModel ( ( e ) => this . onModelChanged ( ) ) ) ;
43+ this . globalToDispose . push ( editor . onDidChangeModelLanguage ( ( e ) => this . onModelChanged ( ) ) ) ;
44+ this . globalToDispose . push ( ColorProviderRegistry . onDidChange ( ( e ) => this . onModelChanged ( ) ) ) ;
3545
3646 this . timeoutPromise = null ;
3747 this . computePromise = null ;
38- this . beginCompute ( ) ;
48+ this . onModelChanged ( ) ;
3949 }
4050
4151 getId ( ) : string {
@@ -47,41 +57,54 @@ export class ColorDetector implements IEditorContribution {
4757 }
4858
4959 dispose ( ) : void {
50- this . listenersToRemove = dispose ( this . listenersToRemove ) ;
5160 this . stop ( ) ;
61+ this . globalToDispose = dispose ( this . globalToDispose ) ;
5262 }
5363
5464 private onModelChanged ( ) : void {
5565 this . stop ( ) ;
56- this . beginCompute ( ) ;
57- }
58-
59- private onModelModeChanged ( ) : void {
60- this . stop ( ) ;
61- this . beginCompute ( ) ;
62- }
63-
64- private onChange ( ) : void {
65- if ( ! this . timeoutPromise ) {
66- this . timeoutPromise = TPromise . timeout ( ColorDetector . RECOMPUTE_TIME ) ;
67- this . timeoutPromise . then ( ( ) => {
68- this . timeoutPromise = null ;
69- this . beginCompute ( ) ;
70- } ) ;
66+ const model = this . editor . getModel ( ) ;
67+ if ( ! model ) {
68+ return ;
7169 }
72- }
7370
74- private beginCompute ( ) : void {
75- if ( ! this . editor . getModel ( ) ) {
71+ if ( ! ColorProviderRegistry . has ( model ) ) {
7672 return ;
7773 }
7874
79- if ( ! ColorProviderRegistry . has ( this . editor . getModel ( ) ) ) {
80- return ;
75+ for ( const provider of ColorProviderRegistry . all ( model ) ) {
76+ if ( typeof provider . onDidChange === 'function' ) {
77+ let registration = provider . onDidChange ( ( ) => {
78+ if ( this . timeoutPromise ) {
79+ this . timeoutPromise . cancel ( ) ;
80+ this . timeoutPromise = null ;
81+ }
82+ if ( this . computePromise ) {
83+ this . computePromise . cancel ( ) ;
84+ this . computePromise = null ;
85+ }
86+ this . beginCompute ( ) ;
87+ } ) ;
88+ this . localToDispose . push ( registration ) ;
89+ }
8190 }
8291
92+ this . localToDispose . push ( this . editor . onDidChangeModelContent ( ( e ) => {
93+ if ( ! this . timeoutPromise ) {
94+ this . timeoutPromise = TPromise . timeout ( ColorDetector . RECOMPUTE_TIME ) ;
95+ this . timeoutPromise . then ( ( ) => {
96+ this . timeoutPromise = null ;
97+ this . beginCompute ( ) ;
98+ } ) ;
99+ }
100+ } ) ) ;
101+ this . beginCompute ( ) ;
102+ }
103+
104+ private beginCompute ( ) : void {
83105 this . computePromise = getColors ( this . editor . getModel ( ) ) . then ( colorInfos => {
84106 this . updateDecorations ( colorInfos ) ;
107+ this . updateColorDecorators ( colorInfos ) ;
85108 this . computePromise = null ;
86109 } ) ;
87110 }
@@ -95,6 +118,7 @@ export class ColorDetector implements IEditorContribution {
95118 this . computePromise . cancel ( ) ;
96119 this . computePromise = null ;
97120 }
121+ this . localToDispose = dispose ( this . localToDispose ) ;
98122 }
99123
100124 private updateDecorations ( colorInfos : IColorRange [ ] ) : void {
@@ -111,8 +135,7 @@ export class ColorDetector implements IEditorContribution {
111135 const colorRanges = colorInfos . map ( c => ( {
112136 range : c . range ,
113137 color : c . color ,
114- formatters : c . formatters ,
115- renderDecorator : c . renderDecorator
138+ formatters : c . formatters
116139 } ) ) ;
117140
118141 this . decorationsIds = this . editor . deltaDecorations ( this . decorationsIds , decorations ) ;
@@ -121,6 +144,56 @@ export class ColorDetector implements IEditorContribution {
121144 this . decorationsIds . forEach ( ( id , i ) => this . colorRanges . set ( id , colorRanges [ i ] ) ) ;
122145 }
123146
147+ private updateColorDecorators ( colorInfos : IColorRange [ ] ) : void {
148+ let decorations = [ ] ;
149+ let newDecorationsTypes : { [ key : string ] : boolean } = { } ;
150+
151+ for ( let i = 0 ; i < colorInfos . length && decorations . length < MAX_DECORATORS ; i ++ ) {
152+ const { red, green, blue, alpha } = colorInfos [ i ] . color ;
153+ const rgba = new RGBA ( Math . round ( red * 255 ) , Math . round ( green * 255 ) , Math . round ( blue * 255 ) , alpha ) ;
154+ let subKey = hash ( rgba ) . toString ( 16 ) ;
155+ let color = `rgba(${ rgba . r } , ${ rgba . g } , ${ rgba . b } , ${ rgba . a } )` ;
156+ let key = 'colorBox-' + subKey ;
157+
158+ if ( ! this . _decorationsTypes [ key ] && ! newDecorationsTypes [ key ] ) {
159+ this . _codeEditorService . registerDecorationType ( key , {
160+ before : {
161+ contentText : ' ' ,
162+ border : 'solid 0.1em #000' ,
163+ margin : '0.1em 0.2em 0 0.2em' ,
164+ width : '0.8em' ,
165+ height : '0.8em' ,
166+ backgroundColor : color
167+ } ,
168+ dark : {
169+ before : {
170+ border : 'solid 0.1em #eee'
171+ }
172+ }
173+ } ) ;
174+ }
175+
176+ newDecorationsTypes [ key ] = true ;
177+ decorations . push ( {
178+ range : {
179+ startLineNumber : colorInfos [ i ] . range . startLineNumber ,
180+ startColumn : colorInfos [ i ] . range . startColumn ,
181+ endLineNumber : colorInfos [ i ] . range . endLineNumber ,
182+ endColumn : colorInfos [ i ] . range . endColumn
183+ } ,
184+ options : this . _codeEditorService . resolveDecorationOptions ( key , true )
185+ } ) ;
186+ }
187+
188+ for ( let subType in this . _decorationsTypes ) {
189+ if ( ! newDecorationsTypes [ subType ] ) {
190+ this . _codeEditorService . removeDecorationType ( subType ) ;
191+ }
192+ }
193+
194+ this . _colorDecorators = this . editor . deltaDecorations ( this . _colorDecorators , decorations ) ;
195+ }
196+
124197 getColorRange ( position : Position ) : IColorRange | null {
125198 const decorations = this . editor . getModel ( )
126199 . getDecorationsInRange ( Range . fromPositions ( position , position ) )
0 commit comments