Skip to content
20 changes: 20 additions & 0 deletions src/vs/editor/common/viewModel/viewModelDecorations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export class ViewModelDecorations implements IDisposable {
}

public getMinimapDecorationsInRange(range: Range): ViewModelDecoration[] {
if (!this._linesCollection) {
// Return empty array if lines collection is not available (e.g., during disposal)
return [];
}
return this._getDecorationsInRange(range, true, false).decorations;
}

Expand All @@ -119,11 +123,27 @@ export class ViewModelDecorations implements IDisposable {
}

public getDecorationsOnLine(lineNumber: number, onlyMinimapDecorations: boolean = false, onlyMarginDecorations: boolean = false): IViewDecorationsCollection {
if (!this._linesCollection) {
// Return empty collection if lines collection is not available (e.g., during disposal)
return {
decorations: [],
inlineDecorations: [],
hasVariableFonts: false
};
}
const range = new Range(lineNumber, this._linesCollection.getViewLineMinColumn(lineNumber), lineNumber, this._linesCollection.getViewLineMaxColumn(lineNumber));
return this._getDecorationsInRange(range, onlyMinimapDecorations, onlyMarginDecorations);
}

private _getDecorationsInRange(viewRange: Range, onlyMinimapDecorations: boolean, onlyMarginDecorations: boolean): IViewDecorationsCollection {
if (!this._linesCollection) {
// Return empty collection if lines collection is not available (e.g., during disposal)
return {
decorations: [],
inlineDecorations: [],
hasVariableFonts: false
};
}
const modelDecorations = this._linesCollection.getDecorationsInRange(viewRange, this.editorId, filterValidationDecorations(this.configuration.options), filterFontDecorations(this.configuration.options), onlyMinimapDecorations, onlyMarginDecorations);
const startLineNumber = viewRange.startLineNumber;
const endLineNumber = viewRange.endLineNumber;
Expand Down
103 changes: 91 additions & 12 deletions src/vs/editor/common/viewModel/viewModelLines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ILineBreaksComputer, ModelLineProjectionData, InjectedText, ILineBreaks
import { ConstantTimePrefixSumComputer } from '../model/prefixSumComputer.js';
import { ViewLineData } from '../viewModel.js';
import { ICoordinatesConverter, IdentityCoordinatesConverter } from '../coordinatesConverter.js';
import { LineTokens } from '../tokens/lineTokens.js';

export interface IViewModelLines extends IDisposable {
createCoordinatesConverter(): ICoordinatesConverter;
Expand Down Expand Up @@ -465,6 +466,24 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines {

// #region ViewLineInfo

private isValidLineIndex(lineIndex: number): boolean {
return lineIndex >= 0 && lineIndex < this.modelLineProjections.length && !!this.modelLineProjections[lineIndex];
}

private createEmptyViewLineData(): ViewLineData {
const lineContent = '';
const emptyTokens = LineTokens.createEmpty(lineContent, this.model.tokenization.getLanguageIdCodec());
return new ViewLineData(
lineContent,
false,
1,
1,
0,
emptyTokens.inflate(),
null
);
}

private getViewLineInfo(viewLineNumber: number): ViewLineInfo {
viewLineNumber = this._toValidViewLineNumber(viewLineNumber);
const r = this.projectedModelLineLineCounts.getIndexOf(viewLineNumber - 1);
Expand All @@ -474,23 +493,35 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines {
}

private getMinColumnOfViewLine(viewLineInfo: ViewLineInfo): number {
return this.modelLineProjections[viewLineInfo.modelLineNumber - 1].getViewLineMinColumn(
const lineIndex = viewLineInfo.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
return 1;
}
return this.modelLineProjections[lineIndex].getViewLineMinColumn(
this.model,
viewLineInfo.modelLineNumber,
viewLineInfo.modelLineWrappedLineIdx
);
}

private getMaxColumnOfViewLine(viewLineInfo: ViewLineInfo): number {
return this.modelLineProjections[viewLineInfo.modelLineNumber - 1].getViewLineMaxColumn(
const lineIndex = viewLineInfo.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
return 1;
}
return this.modelLineProjections[lineIndex].getViewLineMaxColumn(
this.model,
viewLineInfo.modelLineNumber,
viewLineInfo.modelLineWrappedLineIdx
);
}

private getModelStartPositionOfViewLine(viewLineInfo: ViewLineInfo): Position {
const line = this.modelLineProjections[viewLineInfo.modelLineNumber - 1];
const lineIndex = viewLineInfo.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
return new Position(viewLineInfo.modelLineNumber, 1);
}
const line = this.modelLineProjections[lineIndex];
const minViewColumn = line.getViewLineMinColumn(
this.model,
viewLineInfo.modelLineNumber,
Expand All @@ -504,7 +535,11 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines {
}

private getModelEndPositionOfViewLine(viewLineInfo: ViewLineInfo): Position {
const line = this.modelLineProjections[viewLineInfo.modelLineNumber - 1];
const lineIndex = viewLineInfo.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
return new Position(viewLineInfo.modelLineNumber, 1);
}
const line = this.modelLineProjections[lineIndex];
const maxViewColumn = line.getViewLineMaxColumn(
this.model,
viewLineInfo.modelLineNumber,
Expand Down Expand Up @@ -726,27 +761,52 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines {

public getViewLineContent(viewLineNumber: number): string {
const info = this.getViewLineInfo(viewLineNumber);
return this.modelLineProjections[info.modelLineNumber - 1].getViewLineContent(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
const lineIndex = info.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
// Return empty string if projection is not available
return '';
}
return this.modelLineProjections[lineIndex].getViewLineContent(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
}

public getViewLineLength(viewLineNumber: number): number {
const info = this.getViewLineInfo(viewLineNumber);
return this.modelLineProjections[info.modelLineNumber - 1].getViewLineLength(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
const lineIndex = info.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
// Return 0 if projection is not available
return 0;
}
return this.modelLineProjections[lineIndex].getViewLineLength(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
}

public getViewLineMinColumn(viewLineNumber: number): number {
const info = this.getViewLineInfo(viewLineNumber);
return this.modelLineProjections[info.modelLineNumber - 1].getViewLineMinColumn(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
const lineIndex = info.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
// Return default min column if projection is not available
return 1;
}
return this.modelLineProjections[lineIndex].getViewLineMinColumn(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
}

public getViewLineMaxColumn(viewLineNumber: number): number {
const info = this.getViewLineInfo(viewLineNumber);
return this.modelLineProjections[info.modelLineNumber - 1].getViewLineMaxColumn(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
const lineIndex = info.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
// Return default max column if projection is not available
return 1;
}
return this.modelLineProjections[lineIndex].getViewLineMaxColumn(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
}

public getViewLineData(viewLineNumber: number): ViewLineData {
const info = this.getViewLineInfo(viewLineNumber);
return this.modelLineProjections[info.modelLineNumber - 1].getViewLineData(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
const lineIndex = info.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
// Return empty ViewLineData if projection is not available
return this.createEmptyViewLineData();
}
return this.modelLineProjections[lineIndex].getViewLineData(this.model, info.modelLineNumber, info.modelLineWrappedLineIdx);
}

public getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): ViewLineData[] {
Expand Down Expand Up @@ -793,6 +853,10 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines {
const lineIndex = r.index;
const remainder = r.remainder;

if (!this.isValidLineIndex(lineIndex)) {
return new Position(viewLineNumber, 1);
}

const line = this.modelLineProjections[lineIndex];

const minColumn = line.getViewLineMinColumn(this.model, lineIndex + 1, remainder);
Expand Down Expand Up @@ -822,8 +886,12 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines {

public convertViewPositionToModelPosition(viewLineNumber: number, viewColumn: number): Position {
const info = this.getViewLineInfo(viewLineNumber);
const lineIndex = info.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
return this.model.validatePosition(new Position(info.modelLineNumber, 1));
}

const inputColumn = this.modelLineProjections[info.modelLineNumber - 1].getModelColumnOfViewPosition(info.modelLineWrappedLineIdx, viewColumn);
const inputColumn = this.modelLineProjections[lineIndex].getModelColumnOfViewPosition(info.modelLineWrappedLineIdx, viewColumn);
// console.log('out -> in ' + viewLineNumber + ',' + viewColumn + ' ===> ' + (lineIndex+1) + ',' + inputColumn);
return this.model.validatePosition(new Position(info.modelLineNumber, inputColumn));
}
Expand Down Expand Up @@ -891,6 +959,9 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines {

public getViewLineNumberOfModelPosition(modelLineNumber: number, modelColumn: number): number {
let lineIndex = modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
return 1;
}
if (this.modelLineProjections[lineIndex].isVisible()) {
// this model line is visible
const deltaLineNumber = 1 + this.projectedModelLineLineCounts.getPrefixSum(lineIndex);
Expand Down Expand Up @@ -979,12 +1050,20 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines {

public getInjectedTextAt(position: Position): InjectedText | null {
const info = this.getViewLineInfo(position.lineNumber);
return this.modelLineProjections[info.modelLineNumber - 1].getInjectedTextAt(info.modelLineWrappedLineIdx, position.column);
const lineIndex = info.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
return null;
}
return this.modelLineProjections[lineIndex].getInjectedTextAt(info.modelLineWrappedLineIdx, position.column);
}

normalizePosition(position: Position, affinity: PositionAffinity): Position {
const info = this.getViewLineInfo(position.lineNumber);
return this.modelLineProjections[info.modelLineNumber - 1].normalizePosition(info.modelLineWrappedLineIdx, position, affinity);
const lineIndex = info.modelLineNumber - 1;
if (!this.isValidLineIndex(lineIndex)) {
return position;
}
return this.modelLineProjections[lineIndex].normalizePosition(info.modelLineWrappedLineIdx, position, affinity);
}

public getLineIndentColumn(lineNumber: number): number {
Expand Down