@@ -10,15 +10,16 @@ import { CursorCollection } from 'vs/editor/common/controller/cursorCollection';
1010import { CursorColumns , CursorConfiguration , CursorContext , CursorState , EditOperationResult , EditOperationType , IColumnSelectData , ICursors , PartialCursorState , RevealTarget } from 'vs/editor/common/controller/cursorCommon' ;
1111import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations' ;
1212import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents' ;
13- import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations' ;
13+ import { TypeOperations , TypeWithAutoClosingCommand } from 'vs/editor/common/controller/cursorTypeOperations' ;
1414import { Position } from 'vs/editor/common/core/position' ;
1515import { Range } from 'vs/editor/common/core/range' ;
1616import { ISelection , Selection , SelectionDirection } from 'vs/editor/common/core/selection' ;
1717import * as editorCommon from 'vs/editor/common/editorCommon' ;
18- import { IIdentifiedSingleEditOperation , ITextModel , TrackedRangeStickiness } from 'vs/editor/common/model' ;
18+ import { IIdentifiedSingleEditOperation , ITextModel , TrackedRangeStickiness , IModelDeltaDecoration } from 'vs/editor/common/model' ;
1919import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents' ;
2020import * as viewEvents from 'vs/editor/common/view/viewEvents' ;
2121import { IViewModel } from 'vs/editor/common/viewModel/viewModel' ;
22+ import { dispose } from 'vs/base/common/lifecycle' ;
2223
2324function containsLineMappingChanged ( events : viewEvents . ViewEvent [ ] ) : boolean {
2425 for ( let i = 0 , len = events . length ; i < len ; i ++ ) {
@@ -83,6 +84,64 @@ export class CursorModelState {
8384 }
8485}
8586
87+ class AutoClosedAction {
88+
89+ private readonly _model : ITextModel ;
90+
91+ private _autoClosedCharactersDecorations : string [ ] ;
92+ private _autoClosedEnclosingDecorations : string [ ] ;
93+
94+ constructor ( model : ITextModel , autoClosedCharactersDecorations : string [ ] , autoClosedEnclosingDecorations : string [ ] ) {
95+ this . _model = model ;
96+ this . _autoClosedCharactersDecorations = autoClosedCharactersDecorations ;
97+ this . _autoClosedEnclosingDecorations = autoClosedEnclosingDecorations ;
98+ }
99+
100+ public dispose ( ) : void {
101+ this . _autoClosedCharactersDecorations = this . _model . deltaDecorations ( this . _autoClosedCharactersDecorations , [ ] ) ;
102+ this . _autoClosedEnclosingDecorations = this . _model . deltaDecorations ( this . _autoClosedEnclosingDecorations , [ ] ) ;
103+ }
104+
105+ public getAutoClosedCharactersRanges ( ) : Range [ ] {
106+ let result : Range [ ] = [ ] ;
107+ for ( let i = 0 ; i < this . _autoClosedCharactersDecorations . length ; i ++ ) {
108+ const decorationRange = this . _model . getDecorationRange ( this . _autoClosedCharactersDecorations [ i ] ) ;
109+ if ( decorationRange ) {
110+ result . push ( decorationRange ) ;
111+ }
112+ }
113+ return result ;
114+ }
115+
116+ public isValid ( selections : Range [ ] ) : boolean {
117+ let enclosingRanges : Range [ ] = [ ] ;
118+ for ( let i = 0 ; i < this . _autoClosedEnclosingDecorations . length ; i ++ ) {
119+ const decorationRange = this . _model . getDecorationRange ( this . _autoClosedEnclosingDecorations [ i ] ) ;
120+ if ( decorationRange ) {
121+ enclosingRanges . push ( decorationRange ) ;
122+ if ( decorationRange . startLineNumber !== decorationRange . endLineNumber ) {
123+ // Stop tracking if the range becomes multiline...
124+ return false ;
125+ }
126+ }
127+ }
128+ enclosingRanges . sort ( Range . compareRangesUsingStarts ) ;
129+
130+ selections . sort ( Range . compareRangesUsingStarts ) ;
131+
132+ for ( let i = 0 ; i < selections . length ; i ++ ) {
133+ if ( i >= enclosingRanges . length ) {
134+ return false ;
135+ }
136+ if ( ! enclosingRanges [ i ] . strictContainsRange ( selections [ i ] ) ) {
137+ return false ;
138+ }
139+ }
140+
141+ return true ;
142+ }
143+ }
144+
86145export class Cursor extends viewEvents . ViewEventEmitter implements ICursors {
87146
88147 public static MAX_CURSOR_COUNT = 10000 ;
@@ -106,6 +165,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
106165 private _isHandling : boolean ;
107166 private _isDoingComposition : boolean ;
108167 private _columnSelectData : IColumnSelectData | null ;
168+ private _autoClosedActions : AutoClosedAction [ ] ;
109169 private _prevEditOperationType : EditOperationType ;
110170
111171 constructor ( configuration : editorCommon . IConfiguration , model : ITextModel , viewModel : IViewModel ) {
@@ -120,6 +180,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
120180 this . _isHandling = false ;
121181 this . _isDoingComposition = false ;
122182 this . _columnSelectData = null ;
183+ this . _autoClosedActions = [ ] ;
123184 this . _prevEditOperationType = EditOperationType . Other ;
124185
125186 this . _register ( this . _model . onDidChangeRawContent ( ( e ) => {
@@ -173,9 +234,24 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
173234
174235 public dispose ( ) : void {
175236 this . _cursors . dispose ( ) ;
237+ this . _autoClosedActions = dispose ( this . _autoClosedActions ) ;
176238 super . dispose ( ) ;
177239 }
178240
241+ private _validateAutoClosedActions ( ) : void {
242+ if ( this . _autoClosedActions . length > 0 ) {
243+ let selections : Range [ ] = this . _cursors . getSelections ( ) ;
244+ for ( let i = 0 ; i < this . _autoClosedActions . length ; i ++ ) {
245+ const autoClosedAction = this . _autoClosedActions [ i ] ;
246+ if ( ! autoClosedAction . isValid ( selections ) ) {
247+ autoClosedAction . dispose ( ) ;
248+ this . _autoClosedActions . splice ( i , 1 ) ;
249+ i -- ;
250+ }
251+ }
252+ }
253+ }
254+
179255 // ------ some getters/setters
180256
181257 public getPrimaryCursor ( ) : CursorState {
@@ -202,6 +278,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
202278 this . _cursors . normalize ( ) ;
203279 this . _columnSelectData = null ;
204280
281+ this . _validateAutoClosedActions ( ) ;
282+
205283 this . _emitStateChangedIfNecessary ( source , reason , oldState ) ;
206284 }
207285
@@ -296,7 +374,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
296374 // a model.setValue() was called
297375 this . _cursors . dispose ( ) ;
298376 this . _cursors = new CursorCollection ( this . context ) ;
299-
377+ this . _validateAutoClosedActions ( ) ;
300378 this . _emitStateChangedIfNecessary ( 'model' , CursorChangeReason . ContentFlush , null ) ;
301379 } else {
302380 const selectionsFromMarkers = this . _cursors . readSelectionFromMarkers ( ) ;
@@ -367,6 +445,35 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
367445 // The commands were applied correctly
368446 this . _interpretCommandResult ( result ) ;
369447
448+ // Check for auto-closing closed characters
449+ let autoClosedCharactersRanges : IModelDeltaDecoration [ ] = [ ] ;
450+ let autoClosedEnclosingRanges : IModelDeltaDecoration [ ] = [ ] ;
451+
452+ for ( let i = 0 ; i < opResult . commands . length ; i ++ ) {
453+ const command = opResult . commands [ i ] ;
454+ if ( command instanceof TypeWithAutoClosingCommand && command . enclosingRange && command . closeCharacterRange ) {
455+ autoClosedCharactersRanges . push ( {
456+ range : command . closeCharacterRange ,
457+ options : {
458+ inlineClassName : 'auto-closed-character' ,
459+ stickiness : TrackedRangeStickiness . NeverGrowsWhenTypingAtEdges
460+ }
461+ } ) ;
462+ autoClosedEnclosingRanges . push ( {
463+ range : command . enclosingRange ,
464+ options : {
465+ stickiness : TrackedRangeStickiness . NeverGrowsWhenTypingAtEdges
466+ }
467+ } ) ;
468+ }
469+ }
470+
471+ if ( autoClosedCharactersRanges . length > 0 ) {
472+ const autoClosedCharactersDecorations = this . _model . deltaDecorations ( [ ] , autoClosedCharactersRanges ) ;
473+ const autoClosedEnclosingDecorations = this . _model . deltaDecorations ( [ ] , autoClosedEnclosingRanges ) ;
474+ this . _autoClosedActions . push ( new AutoClosedAction ( this . _model , autoClosedCharactersDecorations , autoClosedEnclosingDecorations ) ) ;
475+ }
476+
370477 this . _prevEditOperationType = opResult . type ;
371478 }
372479
@@ -540,6 +647,8 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
540647 this . _cursors . startTrackingSelections ( ) ;
541648 }
542649
650+ this . _validateAutoClosedActions ( ) ;
651+
543652 if ( this . _emitStateChangedIfNecessary ( source , cursorChangeReason , oldState ) ) {
544653 this . _revealRange ( RevealTarget . Primary , viewEvents . VerticalRevealType . Simple , true , editorCommon . ScrollType . Smooth ) ;
545654 }
@@ -566,8 +675,15 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
566675 chr = text . charAt ( i ) ;
567676 }
568677
678+ let autoClosedCharacters : Range [ ] = [ ] ;
679+ if ( this . _autoClosedActions . length > 0 ) {
680+ for ( let i = 0 , len = this . _autoClosedActions . length ; i < len ; i ++ ) {
681+ autoClosedCharacters = autoClosedCharacters . concat ( this . _autoClosedActions [ i ] . getAutoClosedCharactersRanges ( ) ) ;
682+ }
683+ }
684+
569685 // Here we must interpret each typed character individually, that's why we create a new context
570- this . _executeEditOperation ( TypeOperations . typeWithInterceptors ( this . _prevEditOperationType , this . context . config , this . context . model , this . getSelections ( ) , chr ) ) ;
686+ this . _executeEditOperation ( TypeOperations . typeWithInterceptors ( this . _prevEditOperationType , this . context . config , this . context . model , this . getSelections ( ) , autoClosedCharacters , chr ) ) ;
571687 }
572688
573689 } else {
0 commit comments