1414
1515'use strict' ;
1616
17- // TODO If we could not depends of TypeScript here, or at least extract just the part we are interested in
18- // We could avoid bundling the entire TypeScript Service with use
19- // see : https://github.com/fdecampredon/brackets-typescript/issues/13
2017
21-
22- declare var require : any ;
2318import ts = require( 'typescript' ) ;
24- import indenter = require( './smartIndenter' ) ;
25-
26-
2719
28- interface Token {
20+ type Token = {
2921 string : string ;
3022 classification : ts . TokenClass ;
3123 length : number ;
3224 position : number ;
3325}
3426
27+ type BracketsStackItem = {
28+ indent : number ; brackets : string [ ]
29+ }
3530
3631interface LineDescriptor {
3732 tokenMap : { [ position : number ] : Token } ;
3833 eolState : ts . EndOfLineState ;
39- text : string ;
34+ indent : number ;
35+ nextLineIndent : number ;
36+ bracketsStack : BracketsStackItem [ ]
4037}
4138
42- function createLineDescriptor ( ) {
43- return {
44- tokenMap : { } ,
45- eolState : ts . EndOfLineState . Start ,
46- text : ''
47- }
48- }
4939
50- function cloneLineDescriptor ( lineDescriptor : LineDescriptor ) : LineDescriptor {
51- return {
52- tokenMap : lineDescriptor . tokenMap ,
53- eolState : lineDescriptor . eolState ,
54- text : lineDescriptor . text
55- }
40+ function last < T > ( arr : T [ ] ) {
41+ return arr [ arr . length - 1 ] ;
5642}
5743
58-
59-
60-
61- var classifier : ts . Classifier = ts . createClassifier ( {
62- log : ( ) => void 0
63- } ) ;
44+ var classifier : ts . Classifier = ts . createClassifier ( { log : ( ) => void 0 } ) ;
6445
6546function getClassificationsForLine ( text : string , eolState : ts . EndOfLineState ) {
6647 var classificationResult = classifier . getClassificationsForLine ( text , eolState ) ,
@@ -137,28 +118,111 @@ function getStyleForToken(token: Token, textBefore: string): string {
137118 }
138119}
139120
140- function initializeLineDescriptor ( lineDescriptor : LineDescriptor , text : string ) {
141- var classificationResult = getClassificationsForLine ( text , lineDescriptor . eolState ) ,
142- tokens = classificationResult . tokens ;
143121
144- if ( lineDescriptor . text ) {
145- lineDescriptor . text += '\n' ;
122+ var openingBrackets = [ '{' , '(' , '[' , '<' ] ;
123+ var closingBrackets = [ '}' , ')' , ']' , '>' ] ;
124+
125+ function isOpening ( bracket : string ) {
126+ return openingBrackets . indexOf ( bracket ) !== - 1 ;
127+ }
128+
129+ function isClosing ( bracket : string ) {
130+ return closingBrackets . indexOf ( bracket ) !== - 1 ;
131+ }
132+
133+
134+ function isPair ( opening : string , closing : string ) {
135+ return openingBrackets . indexOf ( opening ) === closingBrackets . indexOf ( closing ) ;
136+ }
137+
138+
139+ function getLineDescriptorInfo ( text : string , eolState : ts . EndOfLineState , indent : number , bracketsStack : BracketsStackItem [ ] ) {
140+ bracketsStack = bracketsStack . map ( item => ( {
141+ indent : item . indent ,
142+ brackets : item . brackets . slice ( )
143+ } ) ) ;
144+
145+ var classificationResult = getClassificationsForLine ( text , eolState ) ;
146+ var tokens = classificationResult . tokens ;
147+ var tokenMap : { [ position : number ] : Token } = { } ;
148+
149+ var openedBrackets : string [ ] = [ ] ;
150+ var closedBrackets : string [ ] = [ ]
151+
152+ function openBracket ( openedBracket : string ) {
153+ openedBrackets . push ( openedBracket ) ;
146154 }
147- lineDescriptor . text += text ;
148- lineDescriptor . eolState = classificationResult . eolState ;
149- lineDescriptor . tokenMap = { } ;
150155
151- for ( var i = 0 , l = tokens . length ; i < l ; i ++ ) {
152- lineDescriptor . tokenMap [ tokens [ i ] . position ] = tokens [ i ] ;
156+ function closeBracket ( closedBracket : string ) {
157+ var openedBracket = last ( openedBrackets )
158+ if ( openedBracket ) {
159+ if ( isPair ( openedBracket , closedBracket ) ) {
160+ openedBrackets . pop ( ) ;
161+ }
162+ } else {
163+ closedBrackets . push ( closedBracket )
164+ }
153165 }
154- }
155- function setParent ( parent : ts . Node ) {
156- parent . getChildren ( ) . forEach ( node => {
157- if ( ! node . parent ) {
158- node . parent = parent ;
166+
167+
168+ for ( var i = 0 , l = tokens . length ; i < l ; i ++ ) {
169+ var token = tokens [ i ] ;
170+ tokenMap [ token . position ] = token ;
171+ if ( token . classification === ts . TokenClass . Punctuation ) {
172+ if ( isClosing ( token . string ) ) {
173+ closeBracket ( token . string ) ;
174+ } else if ( isOpening ( token . string ) ) {
175+ openBracket ( token . string ) ;
176+ }
159177 }
160- setParent ( node ) ;
161- } )
178+ }
179+
180+
181+
182+ if ( closedBrackets . length ) {
183+ var newStack : string [ ] [ ] = [ ] ;
184+ for ( var i = bracketsStack . length - 1 ; i >= 0 ; i -- ) {
185+ var item = bracketsStack [ i ] ;
186+ var brackets = item . brackets ;
187+
188+ var hasPair = false ;
189+ while (
190+ isPair ( last ( brackets ) , closedBrackets [ 0 ] ) &&
191+ brackets . length && item . brackets . length
192+ ) {
193+ brackets . pop ( ) ;
194+ closedBrackets . shift ( ) ;
195+ hasPair = true ;
196+ }
197+
198+ if ( hasPair ) {
199+ indent = item . indent ;
200+ }
201+
202+ if ( ! brackets . length ) {
203+ bracketsStack . pop ( ) ;
204+ } else {
205+ // in this case we had closing token that are not pair with our openingStack
206+ // error
207+ break ;
208+ }
209+ }
210+ }
211+
212+ if ( openedBrackets . length ) {
213+ bracketsStack . push ( {
214+ indent : indent ,
215+ brackets : openedBrackets
216+ } ) ;
217+ }
218+
219+ return {
220+ eolState : classificationResult . eolState ,
221+ tokenMap : tokenMap ,
222+ indent : indent ,
223+ bracketsStack : bracketsStack ,
224+ hasOpening : ! ! openedBrackets . length
225+ }
162226}
163227
164228function createTypeScriptMode ( options : CodeMirror . EditorConfiguration , spec : any ) : CodeMirror . CodeMirrorMode < any > {
@@ -168,17 +232,35 @@ function createTypeScriptMode(options: CodeMirror.EditorConfiguration, spec: any
168232 blockCommentEnd : '*/' ,
169233 electricChars : ':{}[]()' ,
170234
171- startState ( ) {
172- return createLineDescriptor ( ) ;
235+ startState ( ) : LineDescriptor {
236+ return {
237+ tokenMap : { } ,
238+ eolState : ts . EndOfLineState . Start ,
239+ indent : 0 ,
240+ nextLineIndent : 0 ,
241+ bracketsStack : [ ]
242+ } ;
173243 } ,
174244
175245 copyState ( lineDescriptor : LineDescriptor ) {
176- return cloneLineDescriptor ( lineDescriptor ) ;
246+ return {
247+ tokenMap : lineDescriptor . tokenMap ,
248+ eolState : lineDescriptor . eolState ,
249+ indent : lineDescriptor . indent ,
250+ nextLineIndent : lineDescriptor . nextLineIndent ,
251+ bracketsStack : lineDescriptor . bracketsStack
252+ }
177253 } ,
178254
179255 token ( stream : CodeMirror . CodeMirrorStream , lineDescriptor : LineDescriptor ) {
180256 if ( stream . sol ( ) ) {
181- initializeLineDescriptor ( lineDescriptor , stream . string ) ;
257+ var info = getLineDescriptorInfo ( stream . string , lineDescriptor . eolState , lineDescriptor . nextLineIndent , lineDescriptor . bracketsStack ) ;
258+
259+ lineDescriptor . eolState = info . eolState ;
260+ lineDescriptor . tokenMap = info . tokenMap ;
261+ lineDescriptor . bracketsStack = info . bracketsStack ;
262+ lineDescriptor . indent = info . indent ;
263+ lineDescriptor . nextLineIndent = info . hasOpening ? info . indent + 1 : info . indent ;
182264 }
183265
184266 var token = lineDescriptor . tokenMap [ stream . pos ] ;
@@ -201,25 +283,15 @@ function createTypeScriptMode(options: CodeMirror.EditorConfiguration, spec: any
201283 if ( lineDescriptor . eolState !== ts . EndOfLineState . Start ) {
202284 return CodeMirror . Pass ;
203285 }
204- var text = lineDescriptor . text + '\n' + ( textAfter || 'fakeIndent' ) ;
205- var position = textAfter ? text . length : text . length - 9 ;
206- var sourceFile = ts . createSourceFile ( Math . random ( ) + '.ts' , text , ts . ScriptTarget . Latest , Math . random ( ) + '' ) ;
207- setParent ( sourceFile ) ;
208- var indent = indenter . getIndentation ( position , sourceFile , {
209- IndentSize : options . indentUnit ,
210- TabSize : options . tabSize ,
211- NewLineCharacter : '\n' ,
212- ConvertTabsToSpaces : ! options . indentWithTabs
213- } ) ;
214-
215- if ( indent === null ) {
216- return CodeMirror . Pass ;
217- }
218- return indent ;
286+
287+ var indent = lineDescriptor . nextLineIndent ;
288+ if ( textAfter ) {
289+ indent = getLineDescriptorInfo ( textAfter , lineDescriptor . eolState , lineDescriptor . nextLineIndent , lineDescriptor . bracketsStack ) . indent ;
290+ }
291+
292+ return indent * options . indentUnit
219293 }
220294 }
221-
222-
223295}
224296
225297export = createTypeScriptMode ;
0 commit comments