From 441ef59e4e04b42570850ce5dd5fcfe0f2f6cea4 Mon Sep 17 00:00:00 2001 From: amartinn Date: Sun, 12 Apr 2026 20:18:59 -0400 Subject: [PATCH 1/2] Fix #32541: toggle block comment when cursor is inside comment --- .../comment/browser/blockCommentCommand.ts | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts b/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts index 79c543e14f69c..054b416f6a2bc 100644 --- a/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts @@ -17,6 +17,7 @@ export class BlockCommentCommand implements ICommand { private readonly _selection: Selection; private readonly _insertSpace: boolean; private _usedEndToken: string | null; + private _isRemove: boolean = false; constructor( selection: Selection, @@ -28,6 +29,97 @@ export class BlockCommentCommand implements ICommand { this._usedEndToken = null; } + public static findEnclosingBlockCommentRange(model: ITextModel, position: Position, languageConfigurationService: ILanguageConfigurationService): Range | null { + const lineNumber = position.lineNumber; + const column = position.column; + + // Get language config + const languageId = model.getLanguageIdAtPosition(lineNumber, column); + const config = languageConfigurationService.getLanguageConfiguration(languageId).comments; + if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) { + return null; + } + + const startToken = config.blockCommentStartToken; + const endToken = config.blockCommentEndToken; + + // Find the opening token by walking backward + let startLine = lineNumber; + let startCol = column; + let foundStart = false; + + // First, search backward on the current line + const currentLineContent = model.getLineContent(startLine); + let idx = Math.min(startCol - 1, currentLineContent.length); + while (idx >= 0) { + if (BlockCommentCommand._haystackHasNeedleAtOffset(currentLineContent, startToken, idx)) { + startCol = idx + 1; // 1-based + foundStart = true; + break; + } + idx--; + } + + if (!foundStart) { + for (let ln = startLine - 1; ln >= 1; ln--) { + const lineContent = model.getLineContent(ln); + idx = lineContent.lastIndexOf(startToken); + if (idx !== -1) { + startLine = ln; + startCol = idx + 1; // 1-based + foundStart = true; + break; + } + } + } + + if (!foundStart) { + return null; + } + + let endLine = startLine; + let endCol = startCol + startToken.length; + let foundEnd = false; + + let currentLine = startLine; + let currentCol = endCol; + + while (currentLine <= model.getLineCount()) { + const lineContent = model.getLineContent(currentLine); + if (currentLine === startLine) { + idx = lineContent.indexOf(endToken, currentCol - 1); + } else { + idx = lineContent.indexOf(endToken); + } + if (idx !== -1) { + endLine = currentLine; + endCol = idx + endToken.length + 1; // 1-based, exclusive end after end token + foundEnd = true; + break; + } + currentLine++; + currentCol = 1; + } + + if (!foundEnd) { + return null; + } + + const commentRange = new Range(startLine, startCol, endLine, endCol); + if (!commentRange.containsPosition(position)) { + return null; + } + + // Return both the content range and token positions + const contentRange = new Range(startLine, startCol + startToken.length, endLine, endCol - endToken.length); + const startTokenPos = new Position(startLine, startCol); + const endTokenPos = new Position(endLine, endCol - endToken.length + 1); // Start of end token + + // Return the full range including tokens + const fullRange = new Range(startLine, startCol, endLine, endCol); + return fullRange; + } + public static _haystackHasNeedleAtOffset(haystack: string, needle: string, offset: number): boolean { if (offset < 0) { return false; @@ -178,10 +270,34 @@ export class BlockCommentCommand implements ICommand { return; } + if (this._selection.isEmpty()) { + const result = BlockCommentCommand.findEnclosingBlockCommentRange(model, this._selection.getPosition(), this.languageConfigurationService); + if (result) { + // Remove start token + const startTokenRange = new Range(result.startLineNumber, result.startColumn, result.startLineNumber, result.startColumn + 2); + builder.addTrackedEditOperation(startTokenRange, ''); + + // Remove end token + const endTokenRange = new Range(result.endLineNumber, result.endColumn - 2, result.endLineNumber, result.endColumn); + builder.addTrackedEditOperation(endTokenRange, ''); + + this._isRemove = true; + return; + } else { + alert('No enclosing comment found, proceeding with normal logic'); + } + } + this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, this._insertSpace, model, builder); } public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { + if (this._isRemove) { + const inverseEditOperations = helper.getInverseEditOperations(); + const srcRange = inverseEditOperations[0].range; + return new Selection(srcRange.startLineNumber, srcRange.startColumn, srcRange.startLineNumber, srcRange.startColumn); + } + const inverseEditOperations = helper.getInverseEditOperations(); if (inverseEditOperations.length === 2) { const startTokenEditOperation = inverseEditOperations[0]; From b320cba0b4f88bd673eda60716e09646b05f8d6d Mon Sep 17 00:00:00 2001 From: amartinn Date: Mon, 13 Apr 2026 10:56:54 -0400 Subject: [PATCH 2/2] Small fixes --- .../comment/browser/blockCommentCommand.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts b/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts index 054b416f6a2bc..3fb180e23e262 100644 --- a/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts @@ -110,14 +110,8 @@ export class BlockCommentCommand implements ICommand { return null; } - // Return both the content range and token positions - const contentRange = new Range(startLine, startCol + startToken.length, endLine, endCol - endToken.length); - const startTokenPos = new Position(startLine, startCol); - const endTokenPos = new Position(endLine, endCol - endToken.length + 1); // Start of end token - // Return the full range including tokens - const fullRange = new Range(startLine, startCol, endLine, endCol); - return fullRange; + return commentRange; } public static _haystackHasNeedleAtOffset(haystack: string, needle: string, offset: number): boolean { @@ -274,18 +268,17 @@ export class BlockCommentCommand implements ICommand { const result = BlockCommentCommand.findEnclosingBlockCommentRange(model, this._selection.getPosition(), this.languageConfigurationService); if (result) { // Remove start token - const startTokenRange = new Range(result.startLineNumber, result.startColumn, result.startLineNumber, result.startColumn + 2); + const startTokenRange = new Range(result.startLineNumber, result.startColumn, result.startLineNumber, result.startColumn + config.blockCommentStartToken.length); builder.addTrackedEditOperation(startTokenRange, ''); // Remove end token - const endTokenRange = new Range(result.endLineNumber, result.endColumn - 2, result.endLineNumber, result.endColumn); + const endTokenRange = new Range(result.endLineNumber, result.endColumn - config.blockCommentEndToken.length, result.endLineNumber, result.endColumn); builder.addTrackedEditOperation(endTokenRange, ''); this._isRemove = true; return; - } else { - alert('No enclosing comment found, proceeding with normal logic'); } + // Else, no enclosing comment found, proceeding with normal logic } this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, this._insertSpace, model, builder);