Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions src/vs/editor/contrib/comment/browser/blockCommentCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -28,6 +29,91 @@ 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 the full range including tokens
return commentRange;
}

public static _haystackHasNeedleAtOffset(haystack: string, needle: string, offset: number): boolean {
if (offset < 0) {
return false;
Expand Down Expand Up @@ -178,10 +264,33 @@ 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 + config.blockCommentStartToken.length);
builder.addTrackedEditOperation(startTokenRange, '');

// Remove end token
const endTokenRange = new Range(result.endLineNumber, result.endColumn - config.blockCommentEndToken.length, result.endLineNumber, result.endColumn);
builder.addTrackedEditOperation(endTokenRange, '');

this._isRemove = true;
return;
}
// Else, 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];
Expand Down