diff --git a/.vscode/launch.json b/.vscode/launch.json index 646b99c7943..a83c164f3c2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,10 +41,7 @@ "port": 5876, "outFiles": [ "${workspaceFolder}/out/**/*.js" - ], - "presentation": { - "hidden": true, - } + ] }, { "type": "node", diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 212cc51ea37..259af6cff37 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -24,6 +24,8 @@ export const VIEW_ID = 'workbench.view.search'; export const SEARCH_EXCLUDE_CONFIG = 'search.exclude'; +const SEARCH_SINGLE_RANGE_DIVIDER = '...'; + export const ISearchService = createDecorator('searchService'); /** @@ -260,24 +262,31 @@ export class TextSearchMatch implements ITextSearchMatch { // Trim preview if this is one match and a single-line match with a preview requested. // Otherwise send the full text, like for replace or for showing multiple previews. // TODO this is fishy. - if (previewOptions && previewOptions.matchLines === 1 && (!Array.isArray(range) || range.length === 1) && isSingleLineRange(range)) { - const oneRange = Array.isArray(range) ? range[0] : range; - + const ranges = Array.isArray(range) ? range : [range]; + if (previewOptions && previewOptions.matchLines === 1 && isSingleLineRangeList(ranges)) { // 1 line preview requested text = getNLines(text, previewOptions.matchLines); + + let result = ''; + let shift = 0; + let lastEnd = 0; const leadingChars = Math.floor(previewOptions.charsPerLine / 5); - const previewStart = Math.max(oneRange.startColumn - leadingChars, 0); - const previewText = text.substring(previewStart, previewOptions.charsPerLine + previewStart); + const matches: ISearchRange[] = []; + for (const range of ranges) { + const previewStart = Math.max(range.startColumn - leadingChars, 0); + const previewEnd = range.startColumn + previewOptions.charsPerLine; + if (previewStart > lastEnd + leadingChars) { + result += SEARCH_SINGLE_RANGE_DIVIDER + text.slice(previewStart, previewEnd); + shift += previewStart - (lastEnd + SEARCH_SINGLE_RANGE_DIVIDER.length); + } else { + result += text.slice(lastEnd, previewEnd); + } - const endColInPreview = (oneRange.endLineNumber - oneRange.startLineNumber + 1) <= previewOptions.matchLines ? - Math.min(previewText.length, oneRange.endColumn - previewStart) : // if number of match lines will not be trimmed by previewOptions - previewText.length; // if number of lines is trimmed + matches.push(new OneLineRange(0, range.startColumn - shift, range.endColumn - shift)); + lastEnd = previewEnd; + } - const oneLineRange = new OneLineRange(0, oneRange.startColumn - previewStart, endColInPreview); - this.preview = { - text: previewText, - matches: Array.isArray(range) ? [oneLineRange] : oneLineRange - }; + this.preview = { text: result, matches: matches.length === 1 ? matches[0] : matches }; } else { const firstMatchLine = Array.isArray(range) ? range[0].startLineNumber : range.startLineNumber; @@ -289,10 +298,15 @@ export class TextSearchMatch implements ITextSearchMatch { } } -function isSingleLineRange(range: ISearchRange | ISearchRange[]): boolean { - return Array.isArray(range) ? - range[0].startLineNumber === range[0].endLineNumber : - range.startLineNumber === range.endLineNumber; +function isSingleLineRangeList(ranges: ISearchRange[]): boolean { + const line = ranges[0].startLineNumber; + for (const r of ranges) { + if (r.startLineNumber !== line || r.endLineNumber !== line) { + return false; + } + } + + return true; } export class SearchRange implements ISearchRange { diff --git a/src/vs/workbench/services/search/test/common/search.test.ts b/src/vs/workbench/services/search/test/common/search.test.ts index 94088dcb341..2656fe8c0c7 100644 --- a/src/vs/workbench/services/search/test/common/search.test.ts +++ b/src/vs/workbench/services/search/test/common/search.test.ts @@ -95,6 +95,20 @@ suite('TextSearchResult', () => { assert.equal((result.preview.matches).endColumn, 3); }); + test('compacts multiple ranges on long lines', () => { + const previewOptions: ITextSearchPreviewOptions = { + matchLines: 1, + charsPerLine: 10 + }; + + const range1 = new SearchRange(5, 4, 5, 7); + const range2 = new SearchRange(5, 53, 5, 56); + const range3 = new SearchRange(5, 61, 5, 64); + const result = new TextSearchMatch('foo bar 1234567890123456789012345678901234567890 foo bar baz bar', [range1, range2, range3], previewOptions); + assert.deepEqual(result.preview.matches, [new SearchRange(0, 4, 0, 7), new SearchRange(0, 19, 0, 22), new SearchRange(0, 27, 0, 30)]); + assert.equal(result.preview.text, 'foo bar 123456...o bar baz bar'); + }); + // test('all lines of multiline match', () => { // const previewOptions: ITextSearchPreviewOptions = { // matchLines: 5,