@@ -37,6 +37,7 @@ define(function (require, exports, module) {
3737 CommandManager = require ( "command/CommandManager" ) ,
3838 EditorManager = require ( "editor/EditorManager" ) ,
3939 StringUtils = require ( "utils/StringUtils" ) ,
40+ TokenStream = require ( "language/TokenStream" ) ,
4041 TokenUtils = require ( "utils/TokenUtils" ) ;
4142
4243 /**
@@ -193,57 +194,60 @@ define(function (require, exports, module) {
193194
194195 /**
195196 * @private
196- * Moves the token context to the token that starts the block-comment. Ctx starts in a block-comment.
197+ * Moves the stream to the token that starts the block-comment.
197198 * Returns the position of the prefix or null if gets to the start of the document and didn't found it.
198- * @param {!{editor:{CodeMirror}, pos:{ch:{string}, line:{number}}, token:{object}} } ctx - token context
199+ * @param {!TokenStream } stream - positioned somewhere within the block comment (may be ON the start or end token)
199200 * @param {!RegExp } prefixExp - a valid regular expression
200201 * @return {?{line: number, ch: number} }
201202 */
202- function _findCommentStart ( ctx , prefixExp ) {
203+ function _findCommentStart ( stream , prefixExp ) {
203204 var result = true ;
204205
205- while ( result && ! ctx . token . string . match ( prefixExp ) ) {
206- result = TokenUtils . moveSkippingWhitespace ( TokenUtils . movePrevToken , ctx ) ;
206+ while ( result && ! stream . token . string . match ( prefixExp ) ) {
207+ result = stream . prevSkipWs ( ) ;
207208 }
208- return result ? { line : ctx . pos . line , ch : ctx . token . start } : null ;
209+ return result ? { line : stream . pos . line , ch : stream . token . start } : null ;
209210 }
210211
211212 /**
212213 * @private
213- * Moves the token context to the token that ends the block-comment. Ctx starts in a block-comment.
214+ * Moves the stream to the token that ends the block-comment.
214215 * Returns the position of the sufix or null if gets to the end of the document and didn't found it.
215- * @param {!{editor:{CodeMirror}, pos:{ch:{string}, line:{number}}, token:{object}} } ctx - token context
216+ * @param {!TokenStream } stream - positioned somewhere within the block comment (may be ON the start or end token)
216217 * @param {!RegExp } suffixExp - a valid regular expression
217218 * @param {!number } suffixLen - length of the suffix
218219 * @return {?{line: number, ch: number} }
219220 */
220- function _findCommentEnd ( ctx , suffixExp , suffixLen ) {
221+ function _findCommentEnd ( stream , suffixExp , suffixLen ) {
221222 var result = true ;
222223
223- while ( result && ! ctx . token . string . match ( suffixExp ) ) {
224- result = TokenUtils . moveSkippingWhitespace ( TokenUtils . moveNextToken , ctx ) ;
224+ while ( result && ! stream . token . string . match ( suffixExp ) ) {
225+ result = stream . nextSkipWs ( ) ;
225226 }
226- return result ? { line : ctx . pos . line , ch : ctx . token . end - suffixLen } : null ;
227+ return result ? { line : stream . pos . line , ch : stream . token . end - suffixLen } : null ;
227228 }
228229
229230 /**
230231 * @private
231- * Moves the token context to the next block-comment if there is one before end.
232- * @param {!{editor:{CodeMirror}, pos:{ch:{string}, line:{number}}, token:{object}} } ctx - token context
232+ * Finds any block comment starting somewhere between the initial stream pos and the given end pos (inclusive).
233+ * The block comment may end after the 'end' pos. Returns true if such a comment was found and leaves stream
234+ * on the block comment open token. Else returns false and leaves stream on the token after 'end'.
235+ * @param {!TokenStream } stream
233236 * @param {!{line: number, ch: number} } end - where to stop searching
234237 * @param {!RegExp } prefixExp - a valid regular expression
235238 * @return {boolean } - true if it found a block-comment
236239 */
237- function _findNextBlockComment ( ctx , end , prefixExp ) {
238- var index = ctx . editor . indexFromPos ( end ) ,
239- inside = ctx . editor . indexFromPos ( ctx . pos ) <= index ,
240+ function _findNextBlockComment ( stream , end , prefixExp ) {
241+ // TODO: don't use TokenStream._editor
242+ var index = stream . _editor . _codeMirror . indexFromPos ( end ) ,
243+ inside = stream . _editor . _codeMirror . indexFromPos ( stream . pos ) <= index ,
240244 result = true ;
241245
242- while ( result && inside && ! ctx . token . string . match ( prefixExp ) ) {
243- result = TokenUtils . moveSkippingWhitespace ( TokenUtils . moveNextToken , ctx ) ;
244- inside = ctx . editor . indexFromPos ( ctx . pos ) <= index ;
246+ while ( result && inside && ! stream . token . string . match ( prefixExp ) ) {
247+ result = stream . nextSkipWs ( ) ;
248+ inside = stream . _editor . _codeMirror . indexFromPos ( stream . pos ) <= index ;
245249 }
246- return result && inside && ! ! ctx . token . string . match ( prefixExp ) ;
250+ return result && inside && ! ! stream . token . string . match ( prefixExp ) ;
247251 }
248252
249253 /**
@@ -268,39 +272,38 @@ define(function (require, exports, module) {
268272
269273 var doc = editor . document ,
270274 sel = editor . getSelection ( ) ,
271- ctx = TokenUtils . getInitialContext ( editor . _codeMirror , { line : sel . start . line , ch : sel . start . ch } ) ,
272- startCtx = TokenUtils . getInitialContext ( editor . _codeMirror , { line : sel . start . line , ch : sel . start . ch } ) ,
273- endCtx = TokenUtils . getInitialContext ( editor . _codeMirror , { line : sel . end . line , ch : sel . end . ch } ) ,
275+ stream = TokenStream . forEditor ( editor , sel . start ) ,
276+ startStream = TokenStream . forEditor ( editor , sel . start ) , // TODO: never moved; we could just store the token
277+ endStream = TokenStream . forEditor ( editor , sel . end ) , // TODO: never moved; we could just store the token
274278 prefixExp = new RegExp ( "^" + StringUtils . regexEscape ( prefix ) , "g" ) ,
275279 suffixExp = new RegExp ( StringUtils . regexEscape ( suffix ) + "$" , "g" ) ,
276280 lineExp = _createLineExpressions ( linePrefixes ) ,
277281 prefixPos = null ,
278282 suffixPos = null ,
279- canComment = false ,
280- invalidComment = false ,
281- lineUncomment = false ,
283+ canComment = false , // if true, block-comment; if false, block-uncomment using prefix/suffixPos
284+ invalidComment = false , // if true, we never make any changes
285+ lineUncomment = false , // if true, ignore canComment and always line-uncomment
282286 newSelection ;
283287
284288 var result , text , line ;
285289
286290 // Move the context to the first non-empty token.
287- if ( ! ctx . token . className && ctx . token . string . trim ( ) . length === 0 ) {
288- result = TokenUtils . moveSkippingWhitespace ( TokenUtils . moveNextToken , ctx ) ;
291+ if ( ! stream . token . className && stream . token . string . trim ( ) . length === 0 ) {
292+ result = stream . nextSkipWs ( ) ;
289293 }
290294
291- // Check if we should just do a line uncomment (if all lines in the selection are commented).
292- if ( lineExp . length && ( _matchExpressions ( ctx . token . string , lineExp ) || _matchExpressions ( endCtx . token . string , lineExp ) ) ) {
293- var startCtxIndex = editor . indexFromPos ( { line : ctx . pos . line , ch : ctx . token . start } ) ;
294- var endCtxIndex = editor . indexFromPos ( { line : endCtx . pos . line , ch : endCtx . token . start + endCtx . token . string . length } ) ;
295-
296- // Find if we aren't actually inside a block-comment
295+ // Check if we should just do a line uncomment (if selected lines are nothing but line comments and optionally whitespace).
296+ // Note: a line comment is always a single token including the "//" (i.e. all line comment tokens match lineExp)
297+ if ( lineExp . length && ( _matchExpressions ( stream . token . string , lineExp ) || _matchExpressions ( endStream . token . string , lineExp ) ) ) {
298+ // Find if line comment(s) are nested inside a block comment - find the first non-line-comment, non-whitespace
299+ // token before our range
297300 result = true ;
298- while ( result && _matchExpressions ( ctx . token . string , lineExp ) ) {
299- result = TokenUtils . moveSkippingWhitespace ( TokenUtils . movePrevToken , ctx ) ;
301+ while ( result && _matchExpressions ( stream . token . string , lineExp ) ) {
302+ result = stream . prevSkipWs ( ) ;
300303 }
301304
302305 // If we aren't in a block-comment.
303- if ( ! result || ctx . token . className !== "comment" || ctx . token . string . match ( suffixExp ) ) {
306+ if ( ! result || stream . token . className !== "comment" || stream . token . string . match ( suffixExp ) ) {
304307 // Is a range of text selected? (vs just an insertion pt)
305308 var hasSelection = ( sel . start . line !== sel . end . line ) || ( sel . start . ch !== sel . end . ch ) ;
306309
@@ -310,7 +313,8 @@ define(function (require, exports, module) {
310313 endLine -- ;
311314 }
312315
313- // Find if all the lines are line-commented.
316+ // Find if there's any UN-commented text in the selection (vs. nothing but whitespace & line comments)
317+ // If not, then we'll just line-uncomment
314318 if ( ! _containsUncommented ( editor , sel . start . line , endLine , lineExp ) ) {
315319 lineUncomment = true ;
316320
@@ -319,57 +323,63 @@ define(function (require, exports, module) {
319323 canComment = true ;
320324 }
321325 } else {
322- prefixPos = _findCommentStart ( startCtx , prefixExp ) ;
323- suffixPos = _findCommentEnd ( startCtx , suffixExp , suffix . length ) ;
326+ // We are inside a block comment, so find its bounds for uncommenting
327+ prefixPos = _findCommentStart ( stream , prefixExp ) ;
328+ suffixPos = _findCommentEnd ( stream , suffixExp , suffix . length ) ;
324329 }
325330
326- // If we are in a selection starting and ending in invalid tokens and with no content (not considering spaces),
327- // find if we are inside a block- comment.
328- } else if ( startCtx . token . className === null && endCtx . token . className === null &&
329- ! editor . posWithinRange ( ctx . pos , startCtx . pos , endCtx . pos ) ) {
330- result = TokenUtils . moveSkippingWhitespace ( TokenUtils . moveNextToken , startCtx ) ;
331+ // If selection encompasses only whitespace (because 'stream' is already outside range after a single nextSkipWs())
332+ // Whitespace tokens are ambiguous: might be inside a block comment (next case below), or might be in uncommented
333+ // code ("nothing was found" part of case after next below). Look ahead of start token to see which.
334+ } else if ( startStream . token . className === null && endStream . token . className === null &&
335+ ! editor . posWithinRange ( stream . pos , startStream . pos , endStream . pos ) ) {
331336
332337 // We found a comment, find the start and end and check if the selection is inside the block-comment.
333- if ( startCtx . token . className === "comment" ) {
334- prefixPos = _findCommentStart ( startCtx , prefixExp ) ;
335- suffixPos = _findCommentEnd ( startCtx , suffixExp , suffix . length ) ;
338+ if ( stream . token . className === "comment" ) {
339+ // We found a comment token after the selection, but does block start before the selection or after it?
340+ // (it can't start IN selection since selection it's all whitespace)
341+ prefixPos = _findCommentStart ( stream , prefixExp ) ;
342+ suffixPos = _findCommentEnd ( stream , suffixExp , suffix . length ) ;
336343
344+ // Uncomment if selection lies inside the comment (else we'll create new block comment)
337345 if ( prefixPos !== null && suffix !== null && ! editor . posWithinRange ( sel . start , prefixPos , suffixPos ) ) {
338346 canComment = true ;
339347 }
340348 } else {
349+ // Whitespace is surrounded by plain code - so we'll create a new block comment
341350 canComment = true ;
342351 }
343352
344- // If the start is inside a comment, find the prefix and suffix positions.
345- } else if ( ctx . token . className === "comment" ) {
346- prefixPos = _findCommentStart ( ctx , prefixExp ) ;
347- suffixPos = _findCommentEnd ( ctx , suffixExp , suffix . length ) ;
353+ // If the start is inside a comment, find its bounds for uncommenting
354+ } else if ( stream . token . className === "comment" ) {
355+ prefixPos = _findCommentStart ( stream , prefixExp ) ;
356+ suffixPos = _findCommentEnd ( stream , suffixExp , suffix . length ) ;
348357
349358 // If not try to find the first comment inside the selection.
350359 } else {
351- result = _findNextBlockComment ( ctx , sel . end , prefixExp ) ;
360+ result = _findNextBlockComment ( stream , sel . end , prefixExp ) ;
352361
353- // If nothing was found is ok to comment.
362+ // If nothing was found then we're creating a new block comment
354363 if ( ! result ) {
355364 canComment = true ;
356365 } else {
357- if ( ! ctx . token . string . match ( prefixExp ) ) {
358- prefixPos = _findCommentStart ( ctx , prefixExp ) ;
366+ // Found a block comment to uncomment
367+ if ( ! stream . token . string . match ( prefixExp ) ) {
368+ prefixPos = _findCommentStart ( stream , prefixExp ) ;
359369 } else {
360- prefixPos = { line : ctx . pos . line , ch : ctx . token . start } ;
370+ prefixPos = { line : stream . pos . line , ch : stream . token . start } ;
361371 }
362- suffixPos = _findCommentEnd ( ctx , suffixExp , suffix . length ) ;
372+ suffixPos = _findCommentEnd ( stream , suffixExp , suffix . length ) ;
363373 }
364374 }
365375
366- // Search if there is another comment in the selection. Do nothing if there is one .
376+ // Search if there is > 1 block comment in the selection. Do nothing if so .
367377 if ( ! canComment && ! invalidComment && ! lineUncomment && suffixPos ) {
368378 var start = { line : suffixPos . line , ch : suffixPos . ch + suffix . length + 1 } ;
369379 if ( editor . posWithinRange ( start , sel . start , sel . end ) ) {
370380 // Start searching at the next token, if there is one.
371- result = TokenUtils . moveSkippingWhitespace ( TokenUtils . moveNextToken , ctx ) &&
372- _findNextBlockComment ( ctx , sel . end , prefixExp ) ;
381+ result = stream . nextSkipWs ( ) &&
382+ _findNextBlockComment ( stream , sel . end , prefixExp ) ;
373383
374384 if ( result ) {
375385 invalidComment = true ;
@@ -487,10 +497,10 @@ define(function (require, exports, module) {
487497 }
488498
489499 // If the selection includes a comment or is already a line selection, delegate to Block-Comment
490- var ctx = TokenUtils . getInitialContext ( editor . _codeMirror , { line : selStart . line , ch : selStart . ch } ) ;
491- var result = TokenUtils . moveSkippingWhitespace ( TokenUtils . moveNextToken , ctx ) ;
492- var className = ctx . token . className ;
493- result = result && _findNextBlockComment ( ctx , selEnd , prefixExp ) ;
500+ var stream = TokenStream . forEditor ( editor , selStart ) ;
501+ var result = stream . nextSkipWs ( ) ;
502+ var className = stream . token . className ;
503+ result = result && _findNextBlockComment ( stream , selEnd , prefixExp ) ;
494504
495505 if ( className === "comment" || result || isLineSelection ) {
496506 blockCommentPrefixSuffix ( editor , prefix , suffix , [ ] ) ;
0 commit comments