diff --git a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts index 9cf5de8659a..3311aa6ffae 100644 --- a/src/vs/editor/contrib/comment/common/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/common/lineCommentCommand.ts @@ -63,34 +63,25 @@ export class LineCommentCommand implements editorCommon.ICommand { * Returns null if any of the lines doesn't support a line comment string. */ public static _gatherPreflightCommentStrings(model: editorCommon.ITokenizedModel, startLineNumber: number, endLineNumber: number): ILinePreflightData[] { - let commentStrForLanguage: string[] = []; + + model.forceTokenization(startLineNumber); + const languageId = model.getLanguageIdAtPosition(startLineNumber, 1); + + const config = LanguageConfigurationRegistry.getComments(languageId); + const commentStr = (config ? config.lineCommentToken : null); + if (!commentStr) { + // Mode does not support line comments + return null; + } + let lines: ILinePreflightData[] = []; for (let i = 0, lineCount = endLineNumber - startLineNumber + 1; i < lineCount; i++) { - let lineNumber = startLineNumber + i; - model.forceTokenization(lineNumber); - let languageId = model.getLanguageIdAtPosition(lineNumber, 1); - - // Find the commentStr for this line, if none is found then bail out: we cannot do line comments - let commentStr: string; - if (commentStrForLanguage[languageId]) { - commentStr = commentStrForLanguage[languageId]; - } else { - let config = LanguageConfigurationRegistry.getComments(languageId); - commentStr = (config ? config.lineCommentToken : null); - if (!commentStr) { - // Mode does not support line comments - return null; - } - - commentStrForLanguage[languageId] = commentStr; - } - - lines.push({ + lines[i] = { ignore: false, commentStr: commentStr, commentStrOffset: 0, commentStrLength: commentStr.length - }); + }; } return lines; diff --git a/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts index 802efd52419..1326d8bda33 100644 --- a/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/common/lineCommentCommand.test.ts @@ -9,6 +9,12 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ILinePreflightData, IPreflightData, ISimpleModel, LineCommentCommand, Type } from 'vs/editor/contrib/comment/common/lineCommentCommand'; import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils'; import { CommentMode } from 'vs/editor/test/common/commentMode'; +import * as modes from 'vs/editor/common/modes'; +import { NULL_STATE } from 'vs/editor/common/modes/nullMode'; +import { TokenizationResult2 } from 'vs/editor/common/core/token'; +import { MockMode } from "vs/editor/test/common/mocks/mockMode"; +import { CommentRule } from "vs/editor/common/modes/languageConfiguration"; +import { LanguageConfigurationRegistry } from "vs/editor/common/modes/languageConfigurationRegistry"; suite('Editor Contrib - Line Comment Command', () => { @@ -827,4 +833,108 @@ suite('Editor Contrib - Line Comment As Block Comment 2', () => { }); }); +suite('Editor Contrib - Line Comment in mixed modes', () => { + const OUTER_LANGUAGE_ID = new modes.LanguageIdentifier('outerMode', 3); + const INNER_LANGUAGE_ID = new modes.LanguageIdentifier('innerMode', 4); + + class OuterMode extends MockMode { + constructor(commentsConfig: CommentRule) { + super(OUTER_LANGUAGE_ID); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + comments: commentsConfig + })); + + this._register(modes.TokenizationRegistry.register(this.getLanguageIdentifier().language, { + getInitialState: (): modes.IState => NULL_STATE, + tokenize: undefined, + tokenize2: (line: string, state: modes.IState): TokenizationResult2 => { + let languageId = (/^ /.test(line) ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID); + + let tokens = new Uint32Array(1 << 1); + tokens[(0 << 1)] = 0; + tokens[(0 << 1) + 1] = ( + (modes.ColorId.DefaultForeground << modes.MetadataConsts.FOREGROUND_OFFSET) + | (languageId.id << modes.MetadataConsts.LANGUAGEID_OFFSET) + ); + return new TokenizationResult2(tokens, state); + } + })); + } + } + + class InnerMode extends MockMode { + constructor(commentsConfig: CommentRule) { + super(INNER_LANGUAGE_ID); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + comments: commentsConfig + })); + } + } + + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let outerMode = new OuterMode({ lineComment: '//', blockComment: ['/*', '*/'] }); + let innerMode = new InnerMode({ lineComment: null, blockComment: ['{/*', '*/}'] }); + testCommand( + lines, + outerMode.getLanguageIdentifier(), + selection, + (sel) => new LineCommentCommand(sel, 4, Type.Toggle), + expectedLines, + expectedSelection + ); + innerMode.dispose(); + outerMode.dispose(); + } + + test('issue #24047 (part 1): Commenting code in JSX files', () => { + testLineCommentCommand( + [ + 'import React from \'react\';', + 'const Loader = () => (', + '