From d9145a291dcef0bad3ace81a3d55727ca294c122 Mon Sep 17 00:00:00 2001 From: Enzo Nunes Date: Fri, 6 Jun 2025 10:22:37 +0100 Subject: [PATCH] Fix line comment action for makefiles (Fixes #234464) (#243283) * Fix line comment action for makefiles (Fixes #234464) Pass languageId to _analyzeLines to distinguish makefiles from the rest of the languages. Add test to _analyzeLines specifically for makefiles. * Remove hardcoded string. Apply fix at LanguageConfigurationService. Add comment rule to specify fixed column token placement. Change test scope to test line command instead of just testing the _analyzeLines method. * change added field to use bool instead of user-chosen offset * add check to remove comment detection * add check to following space removal * update branch. add config interface for new noindent option. adapt existing logic for new config format. * fix small issue with following space removal * polish --------- Co-authored-by: Aiday Marlen Kyzy --- extensions/make/language-configuration.json | 5 ++- .../common/languages/languageConfiguration.ts | 20 +++++++++-- .../languageConfigurationRegistry.ts | 8 ++++- .../comment/browser/lineCommentCommand.ts | 15 +++++--- .../test/browser/lineCommentCommand.test.ts | 24 +++++++++++-- src/vs/monaco.d.ts | 20 +++++++++-- .../languageConfigurationExtensionPoint.ts | 36 +++++++++++++++---- 7 files changed, 109 insertions(+), 19 deletions(-) diff --git a/extensions/make/language-configuration.json b/extensions/make/language-configuration.json index 82645b84dfb..05881278a62 100644 --- a/extensions/make/language-configuration.json +++ b/extensions/make/language-configuration.json @@ -1,6 +1,9 @@ { "comments": { - "lineComment": "#" + "lineComment": { + "comment": "#", + "noIndent": true + } }, "brackets": [ [ diff --git a/src/vs/editor/common/languages/languageConfiguration.ts b/src/vs/editor/common/languages/languageConfiguration.ts index b6aa902f78a..c569efd68c1 100644 --- a/src/vs/editor/common/languages/languageConfiguration.ts +++ b/src/vs/editor/common/languages/languageConfiguration.ts @@ -7,14 +7,30 @@ import { CharCode } from '../../../base/common/charCode.js'; import { StandardTokenType } from '../encodedTokenAttributes.js'; import { ScopedLineTokens } from './supports.js'; +/** + * Configuration for line comments. + */ +export interface LineCommentConfig { + /** + * The line comment token, like `//` + */ + comment: string; + /** + * Whether the comment token should not be indented and placed at the first column. + * Defaults to false. + */ + noIndent?: boolean; +} + /** * Describes how comments for a language work. */ export interface CommentRule { /** - * The line comment token, like `// this is a comment` + * The line comment token, like `// this is a comment`. + * Can be a string or an object with comment and optional noIndent properties. */ - lineComment?: string | null; + lineComment?: string | LineCommentConfig | null; /** * The block comment character pair, like `/* block comment */` */ diff --git a/src/vs/editor/common/languages/languageConfigurationRegistry.ts b/src/vs/editor/common/languages/languageConfigurationRegistry.ts index f42b06d0b74..a545b571a8e 100644 --- a/src/vs/editor/common/languages/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/languages/languageConfigurationRegistry.ts @@ -27,6 +27,7 @@ import { LanguageBracketsConfiguration } from './supports/languageBracketsConfig */ export interface ICommentsConfiguration { lineCommentToken?: string; + lineCommentNoIndent?: boolean; blockCommentStartToken?: string; blockCommentEndToken?: string; } @@ -456,7 +457,12 @@ export class ResolvedLanguageConfiguration { const comments: ICommentsConfiguration = {}; if (commentRule.lineComment) { - comments.lineCommentToken = commentRule.lineComment; + if (typeof commentRule.lineComment === 'string') { + comments.lineCommentToken = commentRule.lineComment; + } else { + comments.lineCommentToken = commentRule.lineComment.comment; + comments.lineCommentNoIndent = commentRule.lineComment.noIndent; + } } if (commentRule.blockComment) { const [blockStart, blockEnd] = commentRule.blockComment; diff --git a/src/vs/editor/contrib/comment/browser/lineCommentCommand.ts b/src/vs/editor/contrib/comment/browser/lineCommentCommand.ts index 53875399585..9b169645d6b 100644 --- a/src/vs/editor/contrib/comment/browser/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/browser/lineCommentCommand.ts @@ -112,9 +112,12 @@ export class LineCommentCommand implements ICommand { * Analyze lines and decide which lines are relevant and what the toggle should do. * Also, build up several offsets and lengths useful in the generation of editor operations. */ - public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number, ignoreEmptyLines: boolean, ignoreFirstLine: boolean, languageConfigurationService: ILanguageConfigurationService): IPreflightData { + public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number, ignoreEmptyLines: boolean, ignoreFirstLine: boolean, languageConfigurationService: ILanguageConfigurationService, languageId: string): IPreflightData { let onlyWhitespaceLines = true; + const config = languageConfigurationService.getLanguageConfiguration(languageId).comments; + const lineCommentNoIndent = config?.lineCommentNoIndent ?? false; + let shouldRemoveComments: boolean; if (type === Type.Toggle) { shouldRemoveComments = true; @@ -140,15 +143,16 @@ export class LineCommentCommand implements ICommand { if (lineContentStartOffset === -1) { // Empty or whitespace only line lineData.ignore = ignoreEmptyLines; - lineData.commentStrOffset = lineContent.length; + lineData.commentStrOffset = lineCommentNoIndent ? 0 : lineContent.length; continue; } onlyWhitespaceLines = false; + const offset = lineCommentNoIndent ? 0 : lineContentStartOffset; lineData.ignore = false; - lineData.commentStrOffset = lineContentStartOffset; + lineData.commentStrOffset = offset; - if (shouldRemoveComments && !BlockCommentCommand._haystackHasNeedleAtOffset(lineContent, lineData.commentStr, lineContentStartOffset)) { + if (shouldRemoveComments && !BlockCommentCommand._haystackHasNeedleAtOffset(lineContent, lineData.commentStr, offset)) { if (type === Type.Toggle) { // Every line so far has been a line comment, but this one is not shouldRemoveComments = false; @@ -190,13 +194,14 @@ export class LineCommentCommand implements ICommand { */ public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number, ignoreEmptyLines: boolean, ignoreFirstLine: boolean, languageConfigurationService: ILanguageConfigurationService): IPreflightData { const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber, languageConfigurationService); + const languageId = model.getLanguageIdAtPosition(startLineNumber, 1); if (lines === null) { return { supported: false }; } - return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines, ignoreFirstLine, languageConfigurationService); + return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines, ignoreFirstLine, languageConfigurationService, languageId); } /** diff --git a/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts index e738c1d865d..0d8bd9f4151 100644 --- a/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts @@ -48,6 +48,11 @@ suite('Editor Contrib - Line Comment Command', () => { (accessor, sel) => new LineCommentCommand(accessor.get(ILanguageConfigurationService), sel, 4, Type.ForceAdd, true, true) ); + const testLineCommentCommandTokenFirstColumn = createTestCommandHelper( + { lineComment: { comment: '!@#', noIndent: true }, blockComment: [''] }, + (accessor, sel) => new LineCommentCommand(accessor.get(ILanguageConfigurationService), sel, 4, Type.Toggle, true, true) + ); + test('comment single line', function () { testLineCommentCommand( [ @@ -81,6 +86,21 @@ suite('Editor Contrib - Line Comment Command', () => { ); }); + test('comment with token column fixed', function () { + testLineCommentCommandTokenFirstColumn( + [ + 'some text', + '\tsome more text' + ], + new Selection(2, 1, 2, 1), + [ + 'some text', + '!@# \tsome more text' + ], + new Selection(2, 5, 2, 5) + ); + }); + function createSimpleModel(lines: string[]): ISimpleModel { return { getLineContent: (lineNumber: number) => { @@ -110,7 +130,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' ', ' c', '\t\td' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true, false, disposable.add(new TestLanguageConfigurationService())); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true, false, disposable.add(new TestLanguageConfigurationService()), 'plaintext'); if (!r.supported) { throw new Error(`unexpected`); } @@ -141,7 +161,7 @@ suite('Editor Contrib - Line Comment Command', () => { ' rem ', ' !@# c', '\t\t!@#d' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true, false, disposable.add(new TestLanguageConfigurationService())); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true, false, disposable.add(new TestLanguageConfigurationService()), 'plaintext'); if (!r.supported) { throw new Error(`unexpected`); } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 115c7b8640d..e204ee859ef 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6709,14 +6709,30 @@ declare namespace monaco.languages { }>; } + /** + * Configuration for line comments. + */ + export interface LineCommentConfig { + /** + * The line comment token, like `//` + */ + comment: string; + /** + * Whether the comment token should not be indented and placed at the first column. + * Defaults to false. + */ + noIndent?: boolean; + } + /** * Describes how comments for a language work. */ export interface CommentRule { /** - * The line comment token, like `// this is a comment` + * The line comment token, like `// this is a comment`. + * Can be a string or an object with comment and optional noIndent properties. */ - lineComment?: string | null; + lineComment?: string | LineCommentConfig | null; /** * The block comment character pair, like `/* block comment */` */ diff --git a/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts b/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts index 80aa77d2019..739ae4a7706 100644 --- a/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts @@ -161,11 +161,22 @@ export class LanguageConfigurationFileHandler extends Disposable { let result: CommentRule | undefined = undefined; if (typeof source.lineComment !== 'undefined') { - if (typeof source.lineComment !== 'string') { - console.warn(`[${languageId}]: language configuration: expected \`comments.lineComment\` to be a string.`); - } else { + if (typeof source.lineComment === 'string') { result = result || {}; result.lineComment = source.lineComment; + } else if (types.isObject(source.lineComment)) { + const lineCommentObj = source.lineComment as any; + if (typeof lineCommentObj.comment === 'string') { + result = result || {}; + result.lineComment = { + comment: lineCommentObj.comment, + noIndent: lineCommentObj.noIndent + }; + } else { + console.warn(`[${languageId}]: language configuration: expected \`comments.lineComment.comment\` to be a string.`); + } + } else { + console.warn(`[${languageId}]: language configuration: expected \`comments.lineComment\` to be a string or an object with comment property.`); } } if (typeof source.blockComment !== 'undefined') { @@ -519,7 +530,7 @@ const schema: IJSONSchema = { comments: { default: { blockComment: ['/*', '*/'], - lineComment: '//' + lineComment: { comment: '//', noIndent: false } }, description: nls.localize('schema.comments', 'Defines the comment symbols'), type: 'object', @@ -536,8 +547,21 @@ const schema: IJSONSchema = { }] }, lineComment: { - type: 'string', - description: nls.localize('schema.lineComment', 'The character sequence that starts a line comment.') + type: 'object', + description: nls.localize('schema.lineComment.object', 'Configuration for line comments.'), + properties: { + comment: { + type: 'string', + description: nls.localize('schema.lineComment.comment', 'The character sequence that starts a line comment.') + }, + noIndent: { + type: 'boolean', + description: nls.localize('schema.lineComment.noIndent', 'Whether the comment token should not be indented and placed at the first column. Defaults to false.'), + default: false + } + }, + required: ['comment'], + additionalProperties: false } } },