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 <amarlenkyzy@microsoft.com>
This commit is contained in:
Enzo Nunes 2025-06-06 10:22:37 +01:00 committed by GitHub
parent 2685e2a6b3
commit d9145a291d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 109 additions and 19 deletions

View File

@ -1,6 +1,9 @@
{
"comments": {
"lineComment": "#"
"lineComment": {
"comment": "#",
"noIndent": true
}
},
"brackets": [
[

View File

@ -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 *&#47;`
*/

View File

@ -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;

View File

@ -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);
}
/**

View File

@ -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`);
}

20
src/vs/monaco.d.ts vendored
View File

@ -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 *&#47;`
*/

View File

@ -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
}
}
},