diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts index 5444e399c6f..5b501192c49 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalWordLinkProvider.ts @@ -48,9 +48,10 @@ export class TerminalWordLinkProvider extends TerminalBaseLinkProvider { for (let x = 0; x < line.length; x++) { line.getCell(x, cellData); const chars = cellData.getChars(); + const width = cellData.getWidth(); // Add a link if this is a separator - if (wordSeparators.indexOf(chars) >= 0) { + if (width !== 0 && wordSeparators.indexOf(chars) >= 0) { if (startX !== -1) { result.push(new TerminalLink({ start: { x: startX + 1, y }, end: { x, y } }, text, this._xterm.buffer.active.viewportY, activateCallback, this._tooltipCallback, false, localize('searchWorkspace', 'Search workspace'), this._configurationService)); text = ''; diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts index cce6ad538ea..ec9f1e4239c 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalWordLinkProvider.test.ts @@ -10,7 +10,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -suite('Workbench - TerminalWordLinkProvider', () => { +suite.only('Workbench - TerminalWordLinkProvider', () => { let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; @@ -21,68 +21,54 @@ suite('Workbench - TerminalWordLinkProvider', () => { instantiationService.stub(IConfigurationService, configurationService); }); - async function assertLink(text: string, expected: { text: string, range: [number, number][] }) { + async function assertLink2(text: string, expected: { text: string, range: [number, number][] }[]) { const xterm = new Terminal(); - const provider = instantiationService.createInstance(TerminalWordLinkProvider, xterm, () => { }, () => { }); + const provider: TerminalWordLinkProvider = instantiationService.createInstance(TerminalWordLinkProvider, xterm, () => { }, () => { }); // Write the text and wait for the parser to finish await new Promise(r => xterm.write(text, r)); - // Calculate positions just outside of link boundaries - const noLinkPositions: IBufferCellPosition[] = [ - { x: expected.range[0][0] - 1, y: expected.range[0][1] }, - { x: expected.range[1][0] + 1, y: expected.range[1][1] } - ]; - - // Ensure outside positions do not detect the link - for (let i = 0; i < noLinkPositions.length; i++) { - const link = await new Promise(r => provider.provideLink(noLinkPositions[i], r)); - assert.equal(link, undefined, `Just outside range boundary should not result in link, link found at (${link?.range.start.x}, ${link?.range.start.y}) to (${link?.range.end.x}, ${link?.range.end.y}) while checking (${noLinkPositions[i].x}, ${noLinkPositions[i].y})\nExpected link text=${expected.text}\nActual link text=${link?.text}`); - } - - // Convert range from [[startx, starty], [endx, endy]] to an IBufferRange - const linkRange: IBufferRange = { - start: { x: expected.range[0][0], y: expected.range[0][1] }, - end: { x: expected.range[1][0], y: expected.range[1][1] }, - }; - - // Calculate positions inside the link boundaries - const linkPositions: IBufferCellPosition[] = [ - linkRange.start, - linkRange.end - ]; - - // Ensure inside positions do detect the link - for (let i = 0; i < linkPositions.length; i++) { - const link = await new Promise(r => provider.provideLink(linkPositions[i], r)); - assert.deepEqual(link?.text, expected.text); - assert.deepEqual(link?.range, linkRange); - } + // Ensure all links are provided + const links = (await new Promise(r => provider.provideLinks(1, r)))!; + assert.equal(links.length, expected.length); + const actual = links.map(e => ({ + text: e.text, + range: e.range + })); + const expectedVerbose = expected.map(e => ({ + text: e.text, + range: { + start: { x: e.range[0][0], y: e.range[0][1] }, + end: { x: e.range[1][0], y: e.range[1][1] }, + } + })); + assert.deepEqual(actual, expectedVerbose); } test('should link words as defined by wordSeparators', async () => { await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' ()[]' } }); - await assertLink('foo', { range: [[1, 1], [3, 1]], text: 'foo' }); - await assertLink(' foo ', { range: [[2, 1], [4, 1]], text: 'foo' }); - await assertLink('(foo)', { range: [[2, 1], [4, 1]], text: 'foo' }); - await assertLink('[foo]', { range: [[2, 1], [4, 1]], text: 'foo' }); - await assertLink('{foo}', { range: [[1, 1], [5, 1]], text: '{foo}' }); + await assertLink2('foo', [{ range: [[1, 1], [3, 1]], text: 'foo' }]); + await assertLink2('foo', [{ range: [[1, 1], [3, 1]], text: 'foo' }]); + await assertLink2(' foo ', [{ range: [[2, 1], [4, 1]], text: 'foo' }]); + await assertLink2('(foo)', [{ range: [[2, 1], [4, 1]], text: 'foo' }]); + await assertLink2('[foo]', [{ range: [[2, 1], [4, 1]], text: 'foo' }]); + await assertLink2('{foo}', [{ range: [[1, 1], [5, 1]], text: '{foo}' }]); await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' ' } }); - await assertLink('foo', { range: [[1, 1], [3, 1]], text: 'foo' }); - await assertLink(' foo ', { range: [[2, 1], [4, 1]], text: 'foo' }); - await assertLink('(foo)', { range: [[1, 1], [5, 1]], text: '(foo)' }); - await assertLink('[foo]', { range: [[1, 1], [5, 1]], text: '[foo]' }); - await assertLink('{foo}', { range: [[1, 1], [5, 1]], text: '{foo}' }); + await assertLink2('foo', [{ range: [[1, 1], [3, 1]], text: 'foo' }]); + await assertLink2(' foo ', [{ range: [[2, 1], [4, 1]], text: 'foo' }]); + await assertLink2('(foo)', [{ range: [[1, 1], [5, 1]], text: '(foo)' }]); + await assertLink2('[foo]', [{ range: [[1, 1], [5, 1]], text: '[foo]' }]); + await assertLink2('{foo}', [{ range: [[1, 1], [5, 1]], text: '{foo}' }]); }); test('should support wide characters', async () => { await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: ' []' } }); - await assertLink('aabbccdd.txt ', { range: [[1, 1], [12, 1]], text: 'aabbccdd.txt' }); - await assertLink('我是学生.txt ', { range: [[1, 1], [12, 1]], text: '我是学生.txt' }); - await assertLink(' aabbccdd.txt ', { range: [[2, 1], [13, 1]], text: 'aabbccdd.txt' }); - await assertLink(' 我是学生.txt ', { range: [[2, 1], [13, 1]], text: '我是学生.txt' }); - await assertLink(' [aabbccdd.txt] ', { range: [[3, 1], [14, 1]], text: 'aabbccdd.txt' }); - await assertLink(' [我是学生.txt] ', { range: [[3, 1], [14, 1]], text: '我是学生.txt' }); + await assertLink2('aabbccdd.txt ', [{ range: [[1, 1], [12, 1]], text: 'aabbccdd.txt' }]); + await assertLink2('我是学生.txt ', [{ range: [[1, 1], [12, 1]], text: '我是学生.txt' }]); + await assertLink2(' aabbccdd.txt ', [{ range: [[2, 1], [13, 1]], text: 'aabbccdd.txt' }]); + await assertLink2(' 我是学生.txt ', [{ range: [[2, 1], [13, 1]], text: '我是学生.txt' }]); + await assertLink2(' [aabbccdd.txt] ', [{ range: [[3, 1], [14, 1]], text: 'aabbccdd.txt' }]); + await assertLink2(' [我是学生.txt] ', [{ range: [[3, 1], [14, 1]], text: '我是学生.txt' }]); }); });