From 7615cd210a3c4dc2c4976b4f082f7029c062aa0a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 2 May 2026 04:34:05 -0700 Subject: [PATCH] Store a boolean on whether the cache is trimmed --- src/common/buffer/BufferLine.test.ts | 55 ++++++++++++++++------------ src/common/buffer/BufferLine.ts | 28 +++++++------- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index 1e7451589..7ac943d25 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -16,20 +16,20 @@ class TestBufferLine extends BufferLine { return this._combined; } - public get cachedCanonicalString(): string | undefined { - return this._cachedCanonicalString; + public get cachedString(): string | undefined { + return this._cachedString; } - public set cachedCanonicalString(value: string | undefined) { - this._cachedCanonicalString = value; + public set cachedString(value: string | undefined) { + this._cachedString = value; } - public get cachedCanonicalTrimmedString(): string | undefined { - return this._cachedCanonicalTrimmedString; + public get isCachedStringTrimmed(): boolean { + return this._isCachedStringTrimmed; } - public set cachedCanonicalTrimmedString(value: string | undefined) { - this._cachedCanonicalTrimmedString = value; + public set isCachedStringTrimmed(value: boolean) { + this._isCachedStringTrimmed = value; } public toArray(): CharData[] { @@ -833,26 +833,32 @@ describe('BufferLine', function(): void { // Trimmed-only canonical request should cache the trimmed value. const trimmed = line.translateToString(true, undefined, undefined, undefined); assert.equal(trimmed, 'abc'); - assert.equal(line.cachedCanonicalTrimmedString, 'abc'); + assert.equal(line.cachedString, 'abc'); + assert.equal(line.isCachedStringTrimmed, true); - // Non-trimmed canonical request should cache the full value and free trimmed cache. + // Non-trimmed canonical request should refresh cache with the full value. const translated = line.translateToString(false, undefined, undefined, undefined); assert.equal(translated, 'abc '); - assert.equal(line.cachedCanonicalString, 'abc '); - assert.equal(line.cachedCanonicalTrimmedString, undefined); + assert.equal(line.cachedString, 'abc '); + assert.equal(line.isCachedStringTrimmed, false); - // Once non-trimmed is cached, trimmed should be derived from trimEnd() (not re-cached). + // Once non-trimmed is cached, trimmed should be derived via trimEnd(). assert.equal(line.translateToString(true, undefined, undefined, undefined), 'abc'); - assert.equal(line.cachedCanonicalTrimmedString, undefined); + assert.equal(line.cachedString, 'abc '); + assert.equal(line.isCachedStringTrimmed, false); - line.cachedCanonicalString = 'cached-non-trimmed '; - line.cachedCanonicalTrimmedString = 'cached-trimmed'; + line.cachedString = 'cached-non-trimmed '; + line.isCachedStringTrimmed = false; assert.equal(line.translateToString(false, undefined, undefined, undefined), 'cached-non-trimmed '); - assert.equal(line.translateToString(true, undefined, undefined, undefined), 'cached-trimmed'); - - line.cachedCanonicalTrimmedString = undefined; assert.equal(line.translateToString(true, undefined, undefined, undefined), 'cached-non-trimmed'); + line.cachedString = 'cached-trimmed'; + line.isCachedStringTrimmed = true; + assert.equal(line.translateToString(true, undefined, undefined, undefined), 'cached-trimmed'); + assert.equal(line.translateToString(false, undefined, undefined, undefined), 'abc '); + assert.equal(line.cachedString, 'abc '); + assert.equal(line.isCachedStringTrimmed, false); + // Any optional translation argument should bypass cache. assert.equal(line.translateToString(false, 0, 2, undefined), 'ab'); assert.equal(line.translateToString(true, 0, 2, undefined), 'ab'); @@ -863,13 +869,14 @@ describe('BufferLine', function(): void { const line = new TestBufferLine(5); line.fill(createCellData(1, 'a', 1)); line.translateToString(true, undefined, undefined, undefined); - assert.equal(line.cachedCanonicalTrimmedString, 'aaaaa'); + assert.equal(line.cachedString, 'aaaaa'); + assert.equal(line.isCachedStringTrimmed, true); line.translateToString(false, undefined, undefined, undefined); - assert.equal(line.cachedCanonicalString, 'aaaaa'); - assert.equal(line.cachedCanonicalTrimmedString, undefined); + assert.equal(line.cachedString, 'aaaaa'); + assert.equal(line.isCachedStringTrimmed, false); mutate(line); - assert.equal(line.cachedCanonicalString, undefined); - assert.equal(line.cachedCanonicalTrimmedString, undefined); + assert.equal(line.cachedString, undefined); + assert.equal(line.isCachedStringTrimmed, false); }; assertCacheInvalidated(line => line.set(0, [0, 'b', 1, 'b'.charCodeAt(0)])); diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 66bf124e3..64639c114 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -63,9 +63,8 @@ export class BufferLine implements IBufferLine { protected _data: Uint32Array; protected _combined: {[index: number]: string} = {}; protected _extendedAttrs: {[index: number]: IExtendedAttrs | undefined} = {}; - // Cache for canonical full-line translations. - protected _cachedCanonicalString: string | undefined; - protected _cachedCanonicalTrimmedString: string | undefined; + protected _cachedString: string | undefined; + protected _isCachedStringTrimmed: boolean = false; public length: number; constructor(cols: number, fillCellData?: ICellData, public isWrapped: boolean = false) { @@ -537,9 +536,13 @@ export class BufferLine implements IBufferLine { */ public translateToString(trimRight?: boolean, startCol?: number, endCol?: number, outColumns?: number[]): string { const isCanonicalRequest = (startCol === undefined || startCol === 0) && endCol === undefined && outColumns === undefined; - const cachedResult = trimRight ? this._cachedCanonicalTrimmedString ?? this._cachedCanonicalString?.trimEnd() : this._cachedCanonicalString; - if (isCanonicalRequest && cachedResult !== undefined) { - return cachedResult; + if (isCanonicalRequest && this._cachedString !== undefined) { + if (trimRight) { + return this._isCachedStringTrimmed ? this._cachedString : this._cachedString.trimEnd(); + } + if (!this._isCachedStringTrimmed) { + return this._cachedString; + } } startCol = startCol ?? 0; endCol = endCol ?? this.length; @@ -566,19 +569,14 @@ export class BufferLine implements IBufferLine { outColumns.push(startCol); } if (isCanonicalRequest) { - if (trimRight) { - this._cachedCanonicalTrimmedString = result; - } else { - this._cachedCanonicalString = result; - // Free the trimmed string as trimEnd() will be used to save memory - this._cachedCanonicalTrimmedString = undefined; - } + this._cachedString = result; + this._isCachedStringTrimmed = !!trimRight; } return result; } private _invalidateStringCache(): void { - this._cachedCanonicalString = undefined; - this._cachedCanonicalTrimmedString = undefined; + this._cachedString = undefined; + this._isCachedStringTrimmed = false; } }