diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index a4cfa6c53ddfe..247490792e4b9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -590,30 +590,27 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer part === null); - if (isFullyRendered) { - if (element.isComplete) { - // Response is done and content is rendered, so do a normal render + const contentIsAlreadyRendered = partsToRender.every(part => part === null); + if (contentIsAlreadyRendered) { + if (contentForThisTurn.moreContentAvailable) { + // The content that we want to render in this turn is already rendered, but there is more content to render on the next tick + this.traceLayout('doNextProgressiveRender', 'not rendering any new content this tick, but more available'); + return false; + } else if (element.isComplete) { + // All content is rendered, and response is done, so do a normal render this.traceLayout('doNextProgressiveRender', `END progressive render, index=${index} and clearing renderData, response is complete`); element.renderData = undefined; this.basicRenderElement(element, index, templateData); return true; - } - - if (!contentForThisTurn.moreContentAvailable) { + } else { // Nothing new to render, stop rendering until next model update this.traceLayout('doNextProgressiveRender', 'caught up with the stream- no new content to render'); return true; } - - // The content that we want to render in this turn is already rendered, but there is more content to render on the next tick - this.traceLayout('doNextProgressiveRender', 'not rendering any new content this tick, but more available'); - return false; } // Do an actual progressive render @@ -690,29 +687,26 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer allWordMatches.length + const endIndex = numWordsToCount >= allWordMatches.length ? str.length // Reached end of string : targetWords.length ? targetWords.at(-1)!.index + targetWords.at(-1)![0].length : 0; diff --git a/src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts b/src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts index b2d51fdf260fe..b538b4c0eb4d8 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { getNWords } from '../../common/chatWordCounter.js'; +import { getNWords, IWordCountResult } from '../../common/chatWordCounter.js'; suite('ChatWordCounter', () => { ensureNoDisposablesAreLeakedInTestSuite(); @@ -23,15 +23,42 @@ suite('ChatWordCounter', () => { ['hello', 1, 'hello'], ['hello world', 0, ''], ['here\'s, some. punctuation?', 3, 'here\'s, some. punctuation?'], - ['| markdown | _table_ | header |', 3, '| markdown | _table_ | header'], + ['| markdown | _table_ | header |', 3, '| markdown | _table_ | header |'], ['| --- | --- | --- |', 1, '| ---'], - ['| --- | --- | --- |', 3, '| --- | --- | ---'], - [' \t some \n whitespace \n\n\nhere ', 3, ' \t some \n whitespace \n\n\nhere'], + ['| --- | --- | --- |', 3, '| --- | --- | --- |'], + [' \t some \n whitespace \n\n\nhere ', 3, ' \t some \n whitespace \n\n\nhere '], ]; cases.forEach(([str, nWords, result]) => doTest(str, nWords, result)); }); + test('whitespace', () => { + assert.deepStrictEqual( + getNWords('hello ', 1), + { + value: 'hello ', + returnedWordCount: 1, + isFullString: true, + totalWordCount: 1, + } satisfies IWordCountResult); + assert.deepStrictEqual( + getNWords('hello\n\n', 1), + { + value: 'hello\n\n', + returnedWordCount: 1, + isFullString: true, + totalWordCount: 1, + } satisfies IWordCountResult); + assert.deepStrictEqual( + getNWords('\nhello', 1), + { + value: '\nhello', + returnedWordCount: 1, + isFullString: true, + totalWordCount: 1, + } satisfies IWordCountResult); + }); + test('matching links', () => { const cases: [string, number, string][] = [ ['[hello](https://example.com) world', 1, '[hello](https://example.com)'],