From 0ee834f5e1e017411c03949e7a198055573c759e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 9 Jul 2017 02:58:01 +0200 Subject: [PATCH 01/17] First attempts --- lib/find.coffee | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/find.coffee b/lib/find.coffee index 86df8378..59ecdd0c 100644 --- a/lib/find.coffee +++ b/lib/find.coffee @@ -18,7 +18,18 @@ module.exports = atom.config.unset('find-and-replace.openProjectFindResultsInRightPane') atom.workspace.addOpener (filePath) -> - new ResultsPaneView() if filePath is ResultsPaneView.URI + # new ResultsPaneView() if filePath is ResultsPaneView.URI + if filePath is ResultsPaneView.URI + editor = atom.workspace.buildTextEditor() + editorView = atom.views.getView editor + editorView.destroy = -> console.log('destroy') + editor.setText('Yo Yo') + editor.getTitle = -> 'Find results' + editor.getPath = -> './find-results.txt' + editor.update({ + showLineNumbers: false, + }) + return editor @subscriptions = new CompositeDisposable @findHistory = new History(findHistory) From 18ffbd66cf94717f382c36127500d5ae328be278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 9 Jul 2017 18:53:54 +0200 Subject: [PATCH 02/17] Moving logic to results-text-builder. Bridging resultsModel to text editor --- lib/find.coffee | 16 ++------ lib/project/results-text-builder.js | 60 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 lib/project/results-text-builder.js diff --git a/lib/find.coffee b/lib/find.coffee index 59ecdd0c..bd479ee7 100644 --- a/lib/find.coffee +++ b/lib/find.coffee @@ -9,6 +9,7 @@ FindView = require './find-view' ProjectFindView = require './project-find-view' ResultsModel = require './project/results-model' ResultsPaneView = require './project/results-pane' +{buildResultsTextEditor} = require './project/results-text-builder' module.exports = activate: ({findOptions, findHistory, replaceHistory, pathsHistory}={}) -> @@ -17,19 +18,10 @@ module.exports = atom.config.set('find-and-replace.projectSearchResultsPaneSplitDirection', 'right') atom.config.unset('find-and-replace.openProjectFindResultsInRightPane') - atom.workspace.addOpener (filePath) -> - # new ResultsPaneView() if filePath is ResultsPaneView.URI + atom.workspace.addOpener (filePath) => if filePath is ResultsPaneView.URI - editor = atom.workspace.buildTextEditor() - editorView = atom.views.getView editor - editorView.destroy = -> console.log('destroy') - editor.setText('Yo Yo') - editor.getTitle = -> 'Find results' - editor.getPath = -> './find-results.txt' - editor.update({ - showLineNumbers: false, - }) - return editor + # return new ResultsPaneView() + return buildResultsTextEditor(@resultsModel) @subscriptions = new CompositeDisposable @findHistory = new History(findHistory) diff --git a/lib/project/results-text-builder.js b/lib/project/results-text-builder.js new file mode 100644 index 00000000..dcc1205b --- /dev/null +++ b/lib/project/results-text-builder.js @@ -0,0 +1,60 @@ +const {CompositeDisposable} = require('atom'); + +const buildResultsTextEditor = (resultsModel) => { + const textEditorRegistry = atom.workspace.textEditorRegistry; + const editor = textEditorRegistry.build(); + console.log(editor); + const subscriptions = new CompositeDisposable(); + subscriptions.add(textEditorRegistry.maintainGrammar(editor)); + subscriptions.add(textEditorRegistry.maintainConfig(editor)); + subscriptions.add(resultsModel.onDidSearchPaths((numberOfPathsSearched) => { + console.log(numberOfPathsSearched); + // subscriptions.add @resultsModel.onDidAddResult ({result}) => + // console.log(result) + // editor.insertText result.filePath + // editor.insertNewline({autoIndentNewline: false}) + // console.log(result.matches) + // result.matches.forEach (match) -> + // match.leadingContextLines.forEach (leadingContextLine) -> + // editor.insertText leadingContextLine + // editor.insertNewline({autoIndentNewline: false}) + // editor.insertText match.lineText + // editor.insertNewline({autoIndentNewline: false}) + // match.trailingContextLines.forEach (trailingContextLine) -> + // editor.insertText trailingContextLine + // editor.insertNewline({autoIndentNewline: false}) + // editor.insertNewline({autoIndentNewline: false}) + })); + + subscriptions.add(resultsModel.onDidFinishSearching((results) => { + console.log(resultsModel.results); + let newText = ''; + resultsModel.getPaths().forEach((filePath) => { + console.log(filePath); + console.log(resultsModel.results[filePath]); + const result = resultsModel.results[filePath]; + newText += filePath + '\n'; + result.matches.forEach((match) => { + match.leadingContextLines.forEach((leadingContextLine) => { + newText += leadingContextLine + '\n'; + }); + newText += match.lineText + '\n'; + match.trailingContextLines.forEach((trailingContextLine) => { + newText += match.lineText + '\n'; + }); + }); + newText += '\n' + }); + editor.setText(newText); + })); + editor.onDidDestroy(() => subscriptions.dispose()); + editorView = atom.views.getView(editor); + editorView.destroy = () => console.log('destroy'); + editor.getTitle = () => 'Project Find Results'; + editor.getIconName = () => 'search'; + return editor; +} + +module.exports = { + buildResultsTextEditor +} From 0cb33143448774df55df519af6ce41e9a7e342f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Mon, 10 Jul 2017 02:19:31 +0200 Subject: [PATCH 03/17] Adding line numbers + some basic styles / grammar --- lib/project/results-text-builder.js | 45 +++++++++++++++---- styles/find-and-replace.atom-text-editor.less | 15 +++++++ 2 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 styles/find-and-replace.atom-text-editor.less diff --git a/lib/project/results-text-builder.js b/lib/project/results-text-builder.js index dcc1205b..eecff688 100644 --- a/lib/project/results-text-builder.js +++ b/lib/project/results-text-builder.js @@ -1,5 +1,25 @@ const {CompositeDisposable} = require('atom'); +const findGrammar = atom.grammars.createGrammar('find', { + 'scopeName': 'find.results', + 'name': 'Find Results', + 'fileTypes': [ + 'results' + ], + 'patterns': [ + { + 'begin': '\x1B', + 'end': '\x1B', + 'name': 'find-results-line-number' + }, + { + 'begin': '\x1C', + 'end': '\x1C', + 'name': 'find-results-path' + } + ] +}); + const buildResultsTextEditor = (resultsModel) => { const textEditorRegistry = atom.workspace.textEditorRegistry; const editor = textEditorRegistry.build(); @@ -28,20 +48,29 @@ const buildResultsTextEditor = (resultsModel) => { subscriptions.add(resultsModel.onDidFinishSearching((results) => { console.log(resultsModel.results); + editor.setGrammar(findGrammar); let newText = ''; resultsModel.getPaths().forEach((filePath) => { console.log(filePath); console.log(resultsModel.results[filePath]); const result = resultsModel.results[filePath]; - newText += filePath + '\n'; + newText += '\x1C' + filePath + ':\x1C\n'; result.matches.forEach((match) => { - match.leadingContextLines.forEach((leadingContextLine) => { - newText += leadingContextLine + '\n'; - }); - newText += match.lineText + '\n'; - match.trailingContextLines.forEach((trailingContextLine) => { - newText += match.lineText + '\n'; - }); + const twoLeadingLines = match.leadingContextLines.slice( + Math.max(match.leadingContextLines.length - 2, 0), + match.leadingContextLines.length + ); + for (let i = 0; i < twoLeadingLines.length; i++) { + newText += '\x1B' + (match.range[0][0] - twoLeadingLines.length + i) + '\x1B ' + twoLeadingLines[i] + '\n'; + }; + newText += '\x1B' + match.range[0][0] + ':\x1B ' + match.lineText + '\n'; + const twoTrailingLines = match.trailingContextLines.slice( + 0, + Math.min(match.trailingContextLines.length, 2) + ); + for (let i = 0; i < twoTrailingLines.length; i++) { + newText += '\x1B' + (match.range[0][0] - twoTrailingLines.length + i) + '\x1B ' + twoTrailingLines[i] + '\n'; + }; }); newText += '\n' }); diff --git a/styles/find-and-replace.atom-text-editor.less b/styles/find-and-replace.atom-text-editor.less new file mode 100644 index 00000000..ae0b168a --- /dev/null +++ b/styles/find-and-replace.atom-text-editor.less @@ -0,0 +1,15 @@ +// Note: This file is specially named! +// Less files with the name `*.atom-text-editor.less` will be loaded into the text-editor! +// +// Since these styles applies to the editor, this file needs to be loaded in the +// context of the editor. +// See example in decoration-example Atom package (not published?) + +// Highlighting ranges of text +// Remember: no default is provided for highlight decorations. +.syntax--find-results-line-number { + color: @syntax-color-variable; +} +.syntax--find-results-path { + color: @syntax-color-value; +} From 2776c936c8795565e61881ea164999edbc72b5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sat, 15 Jul 2017 19:23:36 +0200 Subject: [PATCH 04/17] Add left pad, keep same tab, group close results --- lib/project/results-text-builder.js | 163 ++++++++++++++++++---------- 1 file changed, 106 insertions(+), 57 deletions(-) diff --git a/lib/project/results-text-builder.js b/lib/project/results-text-builder.js index eecff688..f6d538b3 100644 --- a/lib/project/results-text-builder.js +++ b/lib/project/results-text-builder.js @@ -20,68 +20,117 @@ const findGrammar = atom.grammars.createGrammar('find', { ] }); +const LEFT_PAD = ' '; +const pad = (str) => { + str = str.toString(); + return LEFT_PAD.substring(0, LEFT_PAD.length - str.length) + str; +} + +let resultsEditor = null; + const buildResultsTextEditor = (resultsModel) => { const textEditorRegistry = atom.workspace.textEditorRegistry; - const editor = textEditorRegistry.build(); - console.log(editor); - const subscriptions = new CompositeDisposable(); - subscriptions.add(textEditorRegistry.maintainGrammar(editor)); - subscriptions.add(textEditorRegistry.maintainConfig(editor)); - subscriptions.add(resultsModel.onDidSearchPaths((numberOfPathsSearched) => { - console.log(numberOfPathsSearched); - // subscriptions.add @resultsModel.onDidAddResult ({result}) => - // console.log(result) - // editor.insertText result.filePath - // editor.insertNewline({autoIndentNewline: false}) - // console.log(result.matches) - // result.matches.forEach (match) -> - // match.leadingContextLines.forEach (leadingContextLine) -> - // editor.insertText leadingContextLine - // editor.insertNewline({autoIndentNewline: false}) - // editor.insertText match.lineText - // editor.insertNewline({autoIndentNewline: false}) - // match.trailingContextLines.forEach (trailingContextLine) -> - // editor.insertText trailingContextLine - // editor.insertNewline({autoIndentNewline: false}) - // editor.insertNewline({autoIndentNewline: false}) - })); + if (!resultsEditor) { + const editor = textEditorRegistry.build(); + console.log(editor); + const subscriptions = new CompositeDisposable(); + subscriptions.add(textEditorRegistry.maintainGrammar(editor)); + subscriptions.add(textEditorRegistry.maintainConfig(editor)); + subscriptions.add(resultsModel.onDidSearchPaths((numberOfPathsSearched) => { + console.log(numberOfPathsSearched); + // subscriptions.add @resultsModel.onDidAddResult ({result}) => + // console.log(result) + // editor.insertText result.filePath + // editor.insertNewline({autoIndentNewline: false}) + // console.log(result.matches) + // result.matches.forEach (match) -> + // match.leadingContextLines.forEach (leadingContextLine) -> + // editor.insertText leadingContextLine + // editor.insertNewline({autoIndentNewline: false}) + // editor.insertText match.lineText + // editor.insertNewline({autoIndentNewline: false}) + // match.trailingContextLines.forEach (trailingContextLine) -> + // editor.insertText trailingContextLine + // editor.insertNewline({autoIndentNewline: false}) + // editor.insertNewline({autoIndentNewline: false}) + })); + + subscriptions.add(resultsModel.onDidFinishSearching((results) => { + editor.setGrammar(findGrammar); + let resultsLines = []; + resultsModel.getPaths().forEach((filePath) => { + const result = resultsModel.results[filePath]; + resultsLines.push('\x1C' + filePath + ':\x1C'); + let lastLineNumber = null; + console.log('s null'); + result.matches.forEach((match) => { + const mainLineNumber = match.range[0][0] + 1; + if (mainLineNumber === lastLineNumber) { + return; + } + console.log('mainLineNumber: ' + mainLineNumber); + + // Remove previous trailing lines that overlap + const linesBetween = mainLineNumber - lastLineNumber - 1; + console.log(linesBetween); + if (lastLineNumber) { + if (linesBetween <= 4) { + // Remove dots separator + resultsLines.pop(); + } + if (linesBetween === 3) { + // Remove one trailing line + resultsLines.pop(); + } else if (linesBetween < 3) { + // Remove two trailing lines + resultsLines.pop(); + resultsLines.pop(); + } + } + lastLineNumber = mainLineNumber; + + // Add new leading lines + const twoLeadingLines = match.leadingContextLines.slice( + Math.max(match.leadingContextLines.length - Math.min(linesBetween, 2), 0), + match.leadingContextLines.length + ); + for (let i = 0; i < twoLeadingLines.length; i++) { + const lineNumber = match.range[0][0] + 1 - twoLeadingLines.length + i; + resultsLines.push('\x1B' + pad(lineNumber) + '\x1B ' + twoLeadingLines[i]); + }; + + // Add main line + resultsLines.push('\x1B' + pad(mainLineNumber) + ':\x1B ' + match.lineText); + + // Add trailing lines + const twoTrailingLines = match.trailingContextLines.slice( + 0, + Math.min(match.trailingContextLines.length, 2) + ); + for (let i = 0; i < twoTrailingLines.length; i++) { + const lineNumber = match.range[0][0] + 1 + i + 1; + resultsLines.push('\x1B' + pad(lineNumber) + '\x1B ' + twoTrailingLines[i]); + }; - subscriptions.add(resultsModel.onDidFinishSearching((results) => { - console.log(resultsModel.results); - editor.setGrammar(findGrammar); - let newText = ''; - resultsModel.getPaths().forEach((filePath) => { - console.log(filePath); - console.log(resultsModel.results[filePath]); - const result = resultsModel.results[filePath]; - newText += '\x1C' + filePath + ':\x1C\n'; - result.matches.forEach((match) => { - const twoLeadingLines = match.leadingContextLines.slice( - Math.max(match.leadingContextLines.length - 2, 0), - match.leadingContextLines.length - ); - for (let i = 0; i < twoLeadingLines.length; i++) { - newText += '\x1B' + (match.range[0][0] - twoLeadingLines.length + i) + '\x1B ' + twoLeadingLines[i] + '\n'; - }; - newText += '\x1B' + match.range[0][0] + ':\x1B ' + match.lineText + '\n'; - const twoTrailingLines = match.trailingContextLines.slice( - 0, - Math.min(match.trailingContextLines.length, 2) - ); - for (let i = 0; i < twoTrailingLines.length; i++) { - newText += '\x1B' + (match.range[0][0] - twoTrailingLines.length + i) + '\x1B ' + twoTrailingLines[i] + '\n'; - }; + // Separator + resultsLines.push(pad('..')); + }); + resultsLines.push(''); }); - newText += '\n' + editor.setText(resultsLines.join('\n')); + })); + editor.onDidDestroy(() => { + console.log('destroy'); + subscriptions.dispose(); + resultsEditor = null; }); - editor.setText(newText); - })); - editor.onDidDestroy(() => subscriptions.dispose()); - editorView = atom.views.getView(editor); - editorView.destroy = () => console.log('destroy'); - editor.getTitle = () => 'Project Find Results'; - editor.getIconName = () => 'search'; - return editor; + editorView = atom.views.getView(editor); + editorView.destroy = () => console.log('destroy'); + editor.getTitle = () => 'Project Find Results'; + editor.getIconName = () => 'search'; + resultsEditor = editor; + } + return resultsEditor; } module.exports = { From 5df7993ad4582e71ca460a95e25d4100f8c4a1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sat, 15 Jul 2017 19:30:33 +0200 Subject: [PATCH 05/17] Cleanup --- lib/project/results-text-builder.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/project/results-text-builder.js b/lib/project/results-text-builder.js index f6d538b3..597a28b4 100644 --- a/lib/project/results-text-builder.js +++ b/lib/project/results-text-builder.js @@ -62,17 +62,14 @@ const buildResultsTextEditor = (resultsModel) => { const result = resultsModel.results[filePath]; resultsLines.push('\x1C' + filePath + ':\x1C'); let lastLineNumber = null; - console.log('s null'); result.matches.forEach((match) => { const mainLineNumber = match.range[0][0] + 1; if (mainLineNumber === lastLineNumber) { return; } - console.log('mainLineNumber: ' + mainLineNumber); // Remove previous trailing lines that overlap const linesBetween = mainLineNumber - lastLineNumber - 1; - console.log(linesBetween); if (lastLineNumber) { if (linesBetween <= 4) { // Remove dots separator @@ -115,6 +112,8 @@ const buildResultsTextEditor = (resultsModel) => { // Separator resultsLines.push(pad('..')); }); + // Pop last separator + resultsLines.pop(); resultsLines.push(''); }); editor.setText(resultsLines.join('\n')); From d8da578aeb23f1709d920d1e350dd692c2a6a159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 16 Jul 2017 19:14:57 +0200 Subject: [PATCH 06/17] =?UTF-8?q?Some=20basic=20=E2=80=9Cdouble=20click=20?= =?UTF-8?q?to=20open=20file=E2=80=9D=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/project/results-text-builder.js | 52 ++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/lib/project/results-text-builder.js b/lib/project/results-text-builder.js index 597a28b4..f445586c 100644 --- a/lib/project/results-text-builder.js +++ b/lib/project/results-text-builder.js @@ -20,6 +20,27 @@ const findGrammar = atom.grammars.createGrammar('find', { ] }); +let line = null; +let lineFilesMap = {}; + +const onDoubleClick = (e) => { + if (lineFilesMap[line]) { + atom.workspace + .open(lineFilesMap[line].filePath) + // , { + // pending, + // split: reverseDirections[atom.config.get('find-and-replace.projectSearchResultsPaneSplitDirection')] + // }) + .then(editor => { + editor.setSelectedBufferRange(lineFilesMap[line].range, {autoscroll: true}) + }); + } +}; + +const onCursorPositionChanged = (e) => { + line = e.newBufferPosition && e.newBufferPosition.row; +}; + const LEFT_PAD = ' '; const pad = (str) => { str = str.toString(); @@ -32,17 +53,22 @@ const buildResultsTextEditor = (resultsModel) => { const textEditorRegistry = atom.workspace.textEditorRegistry; if (!resultsEditor) { const editor = textEditorRegistry.build(); - console.log(editor); + editor.getTitle = () => 'Project Find Results'; + editor.getIconName = () => 'search'; + editor.shouldPromptToSave = () => false; + editor.element.addEventListener('dblclick', onDoubleClick); const subscriptions = new CompositeDisposable(); - subscriptions.add(textEditorRegistry.maintainGrammar(editor)); - subscriptions.add(textEditorRegistry.maintainConfig(editor)); + subscriptions.add(atom.textEditors.add(editor)); + subscriptions.add(editor.onDidChangeCursorPosition(onCursorPositionChanged)); + subscriptions.add(editor.onDidDestroy(() => { + editor.element.removeEventListener('dblclick', onDoubleClick); + subscriptions.dispose(); + resultsEditor = null; + })); subscriptions.add(resultsModel.onDidSearchPaths((numberOfPathsSearched) => { - console.log(numberOfPathsSearched); // subscriptions.add @resultsModel.onDidAddResult ({result}) => - // console.log(result) // editor.insertText result.filePath // editor.insertNewline({autoIndentNewline: false}) - // console.log(result.matches) // result.matches.forEach (match) -> // match.leadingContextLines.forEach (leadingContextLine) -> // editor.insertText leadingContextLine @@ -98,6 +124,11 @@ const buildResultsTextEditor = (resultsModel) => { // Add main line resultsLines.push('\x1B' + pad(mainLineNumber) + ':\x1B ' + match.lineText); + // Store the file path and range info for retrieving it on double click + lineFilesMap[resultsLines.length - 1] = { + range: match.range, + filePath: filePath + } // Add trailing lines const twoTrailingLines = match.trailingContextLines.slice( @@ -118,15 +149,6 @@ const buildResultsTextEditor = (resultsModel) => { }); editor.setText(resultsLines.join('\n')); })); - editor.onDidDestroy(() => { - console.log('destroy'); - subscriptions.dispose(); - resultsEditor = null; - }); - editorView = atom.views.getView(editor); - editorView.destroy = () => console.log('destroy'); - editor.getTitle = () => 'Project Find Results'; - editor.getIconName = () => 'search'; resultsEditor = editor; } return resultsEditor; From 94e6ae4540a88d481c3fd019401a09358f99a7b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 16 Jul 2017 23:51:29 +0200 Subject: [PATCH 07/17] Different styles between main line and context lines --- lib/project/results-text-builder.js | 11 ++++++++--- styles/find-and-replace.atom-text-editor.less | 7 +++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/project/results-text-builder.js b/lib/project/results-text-builder.js index f445586c..15dfa708 100644 --- a/lib/project/results-text-builder.js +++ b/lib/project/results-text-builder.js @@ -10,7 +10,12 @@ const findGrammar = atom.grammars.createGrammar('find', { { 'begin': '\x1B', 'end': '\x1B', - 'name': 'find-results-line-number' + 'name': 'find-results-main-line-number' + }, + { + 'begin': '\x1D', + 'end': '\x1D', + 'name': 'find-results-context-line-number' }, { 'begin': '\x1C', @@ -119,7 +124,7 @@ const buildResultsTextEditor = (resultsModel) => { ); for (let i = 0; i < twoLeadingLines.length; i++) { const lineNumber = match.range[0][0] + 1 - twoLeadingLines.length + i; - resultsLines.push('\x1B' + pad(lineNumber) + '\x1B ' + twoLeadingLines[i]); + resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoLeadingLines[i]); }; // Add main line @@ -137,7 +142,7 @@ const buildResultsTextEditor = (resultsModel) => { ); for (let i = 0; i < twoTrailingLines.length; i++) { const lineNumber = match.range[0][0] + 1 + i + 1; - resultsLines.push('\x1B' + pad(lineNumber) + '\x1B ' + twoTrailingLines[i]); + resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoTrailingLines[i]); }; // Separator diff --git a/styles/find-and-replace.atom-text-editor.less b/styles/find-and-replace.atom-text-editor.less index ae0b168a..40068ed2 100644 --- a/styles/find-and-replace.atom-text-editor.less +++ b/styles/find-and-replace.atom-text-editor.less @@ -7,9 +7,12 @@ // Highlighting ranges of text // Remember: no default is provided for highlight decorations. -.syntax--find-results-line-number { +.syntax--find-results-main-line-number { color: @syntax-color-variable; } +.syntax--find-results-context-line-number { + color: fadeout(@syntax-color-variable, 30%); +} .syntax--find-results-path { - color: @syntax-color-value; + color: @syntax-color-import; } From 69c7a8ae6941cb1bd765ea321361bc3400de34eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Mon, 17 Jul 2017 01:52:29 +0200 Subject: [PATCH 08/17] Create ResultsTextViewManager --- lib/find.coffee | 7 +- lib/project/results-text-builder.js | 164 ------------------- lib/project/results-text-view-manager.js | 190 +++++++++++++++++++++++ 3 files changed, 195 insertions(+), 166 deletions(-) delete mode 100644 lib/project/results-text-builder.js create mode 100644 lib/project/results-text-view-manager.js diff --git a/lib/find.coffee b/lib/find.coffee index bd479ee7..621e0745 100644 --- a/lib/find.coffee +++ b/lib/find.coffee @@ -9,7 +9,7 @@ FindView = require './find-view' ProjectFindView = require './project-find-view' ResultsModel = require './project/results-model' ResultsPaneView = require './project/results-pane' -{buildResultsTextEditor} = require './project/results-text-builder' +ResultsTextViewManager = require './project/results-text-view-manager' module.exports = activate: ({findOptions, findHistory, replaceHistory, pathsHistory}={}) -> @@ -21,7 +21,7 @@ module.exports = atom.workspace.addOpener (filePath) => if filePath is ResultsPaneView.URI # return new ResultsPaneView() - return buildResultsTextEditor(@resultsModel) + return @resultsTextViewManager.getResultsTextEditor() @subscriptions = new CompositeDisposable @findHistory = new History(findHistory) @@ -32,6 +32,8 @@ module.exports = @findModel = new BufferSearch(@findOptions) @resultsModel = new ResultsModel(@findOptions) + @resultsTextViewManager = new ResultsTextViewManager(@resultsModel) + @subscriptions.add atom.workspace.getCenter().observeActivePaneItem (paneItem) => if paneItem?.getBuffer?() @findModel.setEditor(paneItem) @@ -177,6 +179,7 @@ module.exports = @projectFindView = null ResultsPaneView.model = null + ResultsTextViewManager.model = null @resultsModel = null @subscriptions?.dispose() diff --git a/lib/project/results-text-builder.js b/lib/project/results-text-builder.js deleted file mode 100644 index 15dfa708..00000000 --- a/lib/project/results-text-builder.js +++ /dev/null @@ -1,164 +0,0 @@ -const {CompositeDisposable} = require('atom'); - -const findGrammar = atom.grammars.createGrammar('find', { - 'scopeName': 'find.results', - 'name': 'Find Results', - 'fileTypes': [ - 'results' - ], - 'patterns': [ - { - 'begin': '\x1B', - 'end': '\x1B', - 'name': 'find-results-main-line-number' - }, - { - 'begin': '\x1D', - 'end': '\x1D', - 'name': 'find-results-context-line-number' - }, - { - 'begin': '\x1C', - 'end': '\x1C', - 'name': 'find-results-path' - } - ] -}); - -let line = null; -let lineFilesMap = {}; - -const onDoubleClick = (e) => { - if (lineFilesMap[line]) { - atom.workspace - .open(lineFilesMap[line].filePath) - // , { - // pending, - // split: reverseDirections[atom.config.get('find-and-replace.projectSearchResultsPaneSplitDirection')] - // }) - .then(editor => { - editor.setSelectedBufferRange(lineFilesMap[line].range, {autoscroll: true}) - }); - } -}; - -const onCursorPositionChanged = (e) => { - line = e.newBufferPosition && e.newBufferPosition.row; -}; - -const LEFT_PAD = ' '; -const pad = (str) => { - str = str.toString(); - return LEFT_PAD.substring(0, LEFT_PAD.length - str.length) + str; -} - -let resultsEditor = null; - -const buildResultsTextEditor = (resultsModel) => { - const textEditorRegistry = atom.workspace.textEditorRegistry; - if (!resultsEditor) { - const editor = textEditorRegistry.build(); - editor.getTitle = () => 'Project Find Results'; - editor.getIconName = () => 'search'; - editor.shouldPromptToSave = () => false; - editor.element.addEventListener('dblclick', onDoubleClick); - const subscriptions = new CompositeDisposable(); - subscriptions.add(atom.textEditors.add(editor)); - subscriptions.add(editor.onDidChangeCursorPosition(onCursorPositionChanged)); - subscriptions.add(editor.onDidDestroy(() => { - editor.element.removeEventListener('dblclick', onDoubleClick); - subscriptions.dispose(); - resultsEditor = null; - })); - subscriptions.add(resultsModel.onDidSearchPaths((numberOfPathsSearched) => { - // subscriptions.add @resultsModel.onDidAddResult ({result}) => - // editor.insertText result.filePath - // editor.insertNewline({autoIndentNewline: false}) - // result.matches.forEach (match) -> - // match.leadingContextLines.forEach (leadingContextLine) -> - // editor.insertText leadingContextLine - // editor.insertNewline({autoIndentNewline: false}) - // editor.insertText match.lineText - // editor.insertNewline({autoIndentNewline: false}) - // match.trailingContextLines.forEach (trailingContextLine) -> - // editor.insertText trailingContextLine - // editor.insertNewline({autoIndentNewline: false}) - // editor.insertNewline({autoIndentNewline: false}) - })); - - subscriptions.add(resultsModel.onDidFinishSearching((results) => { - editor.setGrammar(findGrammar); - let resultsLines = []; - resultsModel.getPaths().forEach((filePath) => { - const result = resultsModel.results[filePath]; - resultsLines.push('\x1C' + filePath + ':\x1C'); - let lastLineNumber = null; - result.matches.forEach((match) => { - const mainLineNumber = match.range[0][0] + 1; - if (mainLineNumber === lastLineNumber) { - return; - } - - // Remove previous trailing lines that overlap - const linesBetween = mainLineNumber - lastLineNumber - 1; - if (lastLineNumber) { - if (linesBetween <= 4) { - // Remove dots separator - resultsLines.pop(); - } - if (linesBetween === 3) { - // Remove one trailing line - resultsLines.pop(); - } else if (linesBetween < 3) { - // Remove two trailing lines - resultsLines.pop(); - resultsLines.pop(); - } - } - lastLineNumber = mainLineNumber; - - // Add new leading lines - const twoLeadingLines = match.leadingContextLines.slice( - Math.max(match.leadingContextLines.length - Math.min(linesBetween, 2), 0), - match.leadingContextLines.length - ); - for (let i = 0; i < twoLeadingLines.length; i++) { - const lineNumber = match.range[0][0] + 1 - twoLeadingLines.length + i; - resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoLeadingLines[i]); - }; - - // Add main line - resultsLines.push('\x1B' + pad(mainLineNumber) + ':\x1B ' + match.lineText); - // Store the file path and range info for retrieving it on double click - lineFilesMap[resultsLines.length - 1] = { - range: match.range, - filePath: filePath - } - - // Add trailing lines - const twoTrailingLines = match.trailingContextLines.slice( - 0, - Math.min(match.trailingContextLines.length, 2) - ); - for (let i = 0; i < twoTrailingLines.length; i++) { - const lineNumber = match.range[0][0] + 1 + i + 1; - resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoTrailingLines[i]); - }; - - // Separator - resultsLines.push(pad('..')); - }); - // Pop last separator - resultsLines.pop(); - resultsLines.push(''); - }); - editor.setText(resultsLines.join('\n')); - })); - resultsEditor = editor; - } - return resultsEditor; -} - -module.exports = { - buildResultsTextEditor -} diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js new file mode 100644 index 00000000..8a7fda47 --- /dev/null +++ b/lib/project/results-text-view-manager.js @@ -0,0 +1,190 @@ +const {CompositeDisposable} = require('atom'); + +const findGrammar = atom.grammars.createGrammar('find', { + 'scopeName': 'find.results', + 'name': 'Find Results', + 'fileTypes': [ + 'results' + ], + 'patterns': [ + { + 'begin': '\x1B', + 'end': '\x1B', + 'name': 'find-results-main-line-number' + }, + { + 'begin': '\x1D', + 'end': '\x1D', + 'name': 'find-results-context-line-number' + }, + { + 'begin': '\x1C', + 'end': '\x1C', + 'name': 'find-results-path' + } + ] +}); + +const LEFT_PAD = ' '; +const pad = (str) => { + str = str.toString(); + return LEFT_PAD.substring(0, LEFT_PAD.length - str.length) + str; +} + +module.exports = +class ResultsTextViewManager { + constructor(model) { + this.model = model; + this.model.setActive(true); + this.editor = null; + this.cursorLine = null; + this.lineToFilesMap = {}; + this.isLoading = false; + // this.searchErrors = []; + // this.searchResults = null; + // this.searchingIsSlow = false; + // this.numberOfPathsSearched = 0; + // this.searchContextLineCountBefore = atom.config.get('find-and-replace.searchContextLineCountBefore') || 0; + // this.searchContextLineCountAfter = atom.config.get('find-and-replace.searchContextLineCountAfter') || 0; + + this.subscriptions = new CompositeDisposable( + // this.model.onDidStartSearching(this.onSearch.bind(this)), + this.model.onDidFinishSearching(this.onFinishedSearching.bind(this)) + // this.model.onDidClear(this.onCleared.bind(this)), + // this.model.onDidClearReplacementState(this.onReplacementStateCleared.bind(this)), + // this.model.onDidSearchPaths(this.onPathsSearched.bind(this)), + // this.model.onDidErrorForPath(error => this.appendError(error.message)), + // atom.config.observe('find-and-replace.searchContextLineCountBefore', this.searchContextLineCountChanged.bind(this)), + // atom.config.observe('find-and-replace.searchContextLineCountAfter', this.searchContextLineCountChanged.bind(this)) + ); + } + + onDoubleClick() { + if (this.cursorLine && this.lineToFilesMap[this.cursorLine]) { + atom.workspace + .open(this.lineToFilesMap[this.cursorLine].filePath) + // , { + // pending, + // split: reverseDirections[atom.config.get('find-and-replace.projectSearchResultsPaneSplitDirection')] + // }) + .then(editor => { + editor.setSelectedBufferRange(this.lineToFilesMap[this.cursorLine].range, {autoscroll: true}) + }); + } + } + + onCursorPositionChanged(e) { + this.cursorLine = e.newBufferPosition && e.newBufferPosition.row; + }; + + onDestroyEditor() { + editor.element.removeEventListener('dblclick', onDoubleClick); + this.editorSubscriptions.dispose(); + this.editor = null; + } + + // onPathsSearched({result}) { + // this.editor.insertText result.filePath + // this.editor.insertNewline({autoIndentNewline: false}) + // result.matches.forEach (match) -> + // match.leadingContextLines.forEach (leadingContextLine) -> + // this.editor.insertText leadingContextLine + // this.editor.insertNewline({autoIndentNewline: false}) + // this.editor.insertText match.lineText + // this.editor.insertNewline({autoIndentNewline: false}) + // match.trailingContextLines.forEach (trailingContextLine) -> + // this.editor.insertText trailingContextLine + // this.editor.insertNewline({autoIndentNewline: false}) + // this.editor.insertNewline({autoIndentNewline: false}) + // } + + onFinishedSearching(results) { + this.editor.setGrammar(findGrammar); + let resultsLines = []; + this.model.getPaths().forEach((filePath) => { + const result = this.model.results[filePath]; + resultsLines.push('\x1C' + filePath + ':\x1C'); + let lastLineNumber = null; + result.matches.forEach((match) => { + const mainLineNumber = match.range[0][0] + 1; + if (mainLineNumber === lastLineNumber) { + return; + } + + // Remove previous trailing lines that overlap + const linesBetween = mainLineNumber - lastLineNumber - 1; + if (lastLineNumber) { + if (linesBetween <= 4) { + // Remove dots separator + resultsLines.pop(); + } + if (linesBetween === 3) { + // Remove one trailing line + resultsLines.pop(); + } else if (linesBetween < 3) { + // Remove two trailing lines + resultsLines.pop(); + resultsLines.pop(); + } + } + lastLineNumber = mainLineNumber; + + // Add new leading lines + const twoLeadingLines = match.leadingContextLines.slice( + Math.max(match.leadingContextLines.length - Math.min(linesBetween, 2), 0), + match.leadingContextLines.length + ); + for (let i = 0; i < twoLeadingLines.length; i++) { + const lineNumber = match.range[0][0] + 1 - twoLeadingLines.length + i; + resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoLeadingLines[i]); + }; + + // Add main line + resultsLines.push('\x1B' + pad(mainLineNumber) + ':\x1B ' + match.lineText); + // Store the file path and range info for retrieving it on double click + this.lineToFilesMap[resultsLines.length - 1] = { + range: match.range, + filePath: filePath + } + + // Add trailing lines + const twoTrailingLines = match.trailingContextLines.slice( + 0, + Math.min(match.trailingContextLines.length, 2) + ); + for (let i = 0; i < twoTrailingLines.length; i++) { + const lineNumber = match.range[0][0] + 1 + i + 1; + resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoTrailingLines[i]); + }; + + // Separator + resultsLines.push(pad('..')); + }); + // Pop last separator + resultsLines.pop(); + resultsLines.push(''); + }); + this.editor.setText(resultsLines.join('\n')); + } + + getResultsTextEditor() { + if (!this.editor) { + this.cursorLine = null; + this.lineToFilesMap = {}; + const textEditorRegistry = atom.workspace.textEditorRegistry; + const editor = textEditorRegistry.build(); + editor.getTitle = () => 'Project Find Results'; + editor.getIconName = () => 'search'; + editor.shouldPromptToSave = () => false; + editor.element.addEventListener('dblclick', this.onDoubleClick.bind(this)); + this.editorSubscriptions = new CompositeDisposable( + editor.onDidChangeCursorPosition(this.onCursorPositionChanged.bind(this)), + editor.onDidDestroy(this.onDestroyEditor.bind(this)) + ); + this.editor = editor; + } + // Start a search in case there are new results + this.onFinishedSearching(this.model.getResultsSummary()); + return this.editor; + } +} From 29e745a41e1a2d7888d98e606d6a73c677820d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Mon, 17 Jul 2017 10:05:33 +0200 Subject: [PATCH 09/17] Remove unneeded removeEventListener --- lib/project/results-text-view-manager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js index 8a7fda47..9e4f6188 100644 --- a/lib/project/results-text-view-manager.js +++ b/lib/project/results-text-view-manager.js @@ -78,7 +78,6 @@ class ResultsTextViewManager { }; onDestroyEditor() { - editor.element.removeEventListener('dblclick', onDoubleClick); this.editorSubscriptions.dispose(); this.editor = null; } From 34e6f4737fae6a72424fc3a8e7a1265288b89f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sat, 22 Jul 2017 01:21:24 +0200 Subject: [PATCH 10/17] Using Range.fromObject --- lib/project/results-text-view-manager.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js index 9e4f6188..7c663969 100644 --- a/lib/project/results-text-view-manager.js +++ b/lib/project/results-text-view-manager.js @@ -1,4 +1,4 @@ -const {CompositeDisposable} = require('atom'); +const { Range, CompositeDisposable } = require('atom'); const findGrammar = atom.grammars.createGrammar('find', { 'scopeName': 'find.results', @@ -105,7 +105,9 @@ class ResultsTextViewManager { resultsLines.push('\x1C' + filePath + ':\x1C'); let lastLineNumber = null; result.matches.forEach((match) => { - const mainLineNumber = match.range[0][0] + 1; + const mainLineNumber = Range.fromObject(match.range).start.row + 1; + + // Ignore more results on the same line if (mainLineNumber === lastLineNumber) { return; } @@ -134,7 +136,7 @@ class ResultsTextViewManager { match.leadingContextLines.length ); for (let i = 0; i < twoLeadingLines.length; i++) { - const lineNumber = match.range[0][0] + 1 - twoLeadingLines.length + i; + const lineNumber = mainLineNumber - twoLeadingLines.length + i; resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoLeadingLines[i]); }; @@ -152,7 +154,7 @@ class ResultsTextViewManager { Math.min(match.trailingContextLines.length, 2) ); for (let i = 0; i < twoTrailingLines.length; i++) { - const lineNumber = match.range[0][0] + 1 + i + 1; + const lineNumber = mainLineNumber + i + 1; resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoTrailingLines[i]); }; From 78c451d60da1e9d09248045f7f1588b76338b8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sat, 22 Jul 2017 17:00:39 +0200 Subject: [PATCH 11/17] Open text results depending on config --- lib/find.coffee | 6 ++++-- lib/project/results-text-view-manager.js | 26 +++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/find.coffee b/lib/find.coffee index 621e0745..2049b857 100644 --- a/lib/find.coffee +++ b/lib/find.coffee @@ -20,8 +20,10 @@ module.exports = atom.workspace.addOpener (filePath) => if filePath is ResultsPaneView.URI - # return new ResultsPaneView() - return @resultsTextViewManager.getResultsTextEditor() + if atom.config.get('find-and-replace.openProjectFindResultsAsText') + return @resultsTextViewManager.getResultsTextEditor() + else + return new ResultsPaneView() @subscriptions = new CompositeDisposable @findHistory = new History(findHistory) diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js index 7c663969..14f5b023 100644 --- a/lib/project/results-text-view-manager.js +++ b/lib/project/results-text-view-manager.js @@ -46,17 +46,6 @@ class ResultsTextViewManager { // this.numberOfPathsSearched = 0; // this.searchContextLineCountBefore = atom.config.get('find-and-replace.searchContextLineCountBefore') || 0; // this.searchContextLineCountAfter = atom.config.get('find-and-replace.searchContextLineCountAfter') || 0; - - this.subscriptions = new CompositeDisposable( - // this.model.onDidStartSearching(this.onSearch.bind(this)), - this.model.onDidFinishSearching(this.onFinishedSearching.bind(this)) - // this.model.onDidClear(this.onCleared.bind(this)), - // this.model.onDidClearReplacementState(this.onReplacementStateCleared.bind(this)), - // this.model.onDidSearchPaths(this.onPathsSearched.bind(this)), - // this.model.onDidErrorForPath(error => this.appendError(error.message)), - // atom.config.observe('find-and-replace.searchContextLineCountBefore', this.searchContextLineCountChanged.bind(this)), - // atom.config.observe('find-and-replace.searchContextLineCountAfter', this.searchContextLineCountChanged.bind(this)) - ); } onDoubleClick() { @@ -78,7 +67,7 @@ class ResultsTextViewManager { }; onDestroyEditor() { - this.editorSubscriptions.dispose(); + this.subscriptions.dispose(); this.editor = null; } @@ -178,10 +167,19 @@ class ResultsTextViewManager { editor.getIconName = () => 'search'; editor.shouldPromptToSave = () => false; editor.element.addEventListener('dblclick', this.onDoubleClick.bind(this)); - this.editorSubscriptions = new CompositeDisposable( + this.subscriptions = new CompositeDisposable( editor.onDidChangeCursorPosition(this.onCursorPositionChanged.bind(this)), - editor.onDidDestroy(this.onDestroyEditor.bind(this)) + editor.onDidDestroy(this.onDestroyEditor.bind(this)), + // this.model.onDidStartSearching(this.onSearch.bind(this)), + this.model.onDidFinishSearching(this.onFinishedSearching.bind(this)) + // this.model.onDidClear(this.onCleared.bind(this)), + // this.model.onDidClearReplacementState(this.onReplacementStateCleared.bind(this)), + // this.model.onDidSearchPaths(this.onPathsSearched.bind(this)), + // this.model.onDidErrorForPath(error => this.appendError(error.message)), + // atom.config.observe('find-and-replace.searchContextLineCountBefore', this.searchContextLineCountChanged.bind(this)), + // atom.config.observe('find-and-replace.searchContextLineCountAfter', this.searchContextLineCountChanged.bind(this)) ); + this.editor = editor; } // Start a search in case there are new results From 0391f49f978f1927e5c796b4774f881dda596b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 23 Jul 2017 17:00:59 +0200 Subject: [PATCH 12/17] Read # lines before and after from atom.config --- lib/project/results-text-view-manager.js | 92 ++++++++++++------------ 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js index 14f5b023..3ff700a3 100644 --- a/lib/project/results-text-view-manager.js +++ b/lib/project/results-text-view-manager.js @@ -44,8 +44,8 @@ class ResultsTextViewManager { // this.searchResults = null; // this.searchingIsSlow = false; // this.numberOfPathsSearched = 0; - // this.searchContextLineCountBefore = atom.config.get('find-and-replace.searchContextLineCountBefore') || 0; - // this.searchContextLineCountAfter = atom.config.get('find-and-replace.searchContextLineCountAfter') || 0; + this.searchContextLineCountBefore = atom.config.get('find-and-replace.searchContextLineCountBefore'); + this.searchContextLineCountAfter = atom.config.get('find-and-replace.searchContextLineCountAfter'); } onDoubleClick() { @@ -88,68 +88,72 @@ class ResultsTextViewManager { onFinishedSearching(results) { this.editor.setGrammar(findGrammar); + const searchContextLineCountTotal = this.searchContextLineCountBefore + this.searchContextLineCountAfter; let resultsLines = []; this.model.getPaths().forEach((filePath) => { const result = this.model.results[filePath]; resultsLines.push('\x1C' + filePath + ':\x1C'); let lastLineNumber = null; - result.matches.forEach((match) => { + for (let i = 0; i < result.matches.length; i++) { + const match = result.matches[i]; const mainLineNumber = Range.fromObject(match.range).start.row + 1; - // Ignore more results on the same line - if (mainLineNumber === lastLineNumber) { - return; - } + // Add leading lines + const linesToPrevMatch = mainLineNumber - lastLineNumber - 1; + const leadingLines = linesToPrevMatch < match.leadingContextLines.length ? + match.leadingContextLines.slice( + match.leadingContextLines.length - linesToPrevMatch, + match.leadingContextLines.length + ) + : match.leadingContextLines; + for (let i = 0; i < leadingLines.length; i++) { + const lineNumber = mainLineNumber - leadingLines.length + i; + resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + leadingLines[i]); + }; - // Remove previous trailing lines that overlap - const linesBetween = mainLineNumber - lastLineNumber - 1; - if (lastLineNumber) { - if (linesBetween <= 4) { - // Remove dots separator - resultsLines.pop(); - } - if (linesBetween === 3) { - // Remove one trailing line - resultsLines.pop(); - } else if (linesBetween < 3) { - // Remove two trailing lines - resultsLines.pop(); - resultsLines.pop(); - } + // Avoid adding the same line multiple times + if (mainLineNumber !== lastLineNumber) { + // Add main line + resultsLines.push('\x1B' + pad(mainLineNumber) + ':\x1B ' + match.lineText); } - lastLineNumber = mainLineNumber; - - // Add new leading lines - const twoLeadingLines = match.leadingContextLines.slice( - Math.max(match.leadingContextLines.length - Math.min(linesBetween, 2), 0), - match.leadingContextLines.length - ); - for (let i = 0; i < twoLeadingLines.length; i++) { - const lineNumber = mainLineNumber - twoLeadingLines.length + i; - resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoLeadingLines[i]); - }; - // Add main line - resultsLines.push('\x1B' + pad(mainLineNumber) + ':\x1B ' + match.lineText); // Store the file path and range info for retrieving it on double click this.lineToFilesMap[resultsLines.length - 1] = { range: match.range, filePath: filePath } + // Check if there is overlap with the next match + // If there is, adjust the number of trailing lines to be added + let linesOverlap = false; + let numberOfTrailingLines = match.trailingContextLines.length; + if (i < result.matches.length - 1) { + const nextMatch = result.matches[i + 1]; + const nextLineNumber = Range.fromObject(nextMatch.range).start.row + 1; + const linesToNextMatch = nextLineNumber - mainLineNumber - 1; + if (linesToNextMatch <= searchContextLineCountTotal) { + linesOverlap = true; + if (linesToNextMatch - this.searchContextLineCountBefore < this.searchContextLineCountAfter) { + numberOfTrailingLines = Math.max( + linesToNextMatch - this.searchContextLineCountBefore, + 0 + ); + } + } + } // Add trailing lines - const twoTrailingLines = match.trailingContextLines.slice( - 0, - Math.min(match.trailingContextLines.length, 2) - ); - for (let i = 0; i < twoTrailingLines.length; i++) { - const lineNumber = mainLineNumber + i + 1; - resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + twoTrailingLines[i]); + for (let j = 0; j < numberOfTrailingLines; j++) { + const lineNumber = mainLineNumber + j + 1; + resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + match.trailingContextLines[j]); }; // Separator - resultsLines.push(pad('..')); - }); + if (!linesOverlap) { + resultsLines.push(pad('..')); + } + + lastLineNumber = mainLineNumber; + }; // Pop last separator resultsLines.pop(); resultsLines.push(''); From dbda9a97b94af395daf53cff88fbbd419a18e63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 23 Jul 2017 17:19:42 +0200 Subject: [PATCH 13/17] =?UTF-8?q?Add=20=E2=80=98no=20results=E2=80=99=20te?= =?UTF-8?q?xt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/project/results-text-view-manager.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js index 3ff700a3..bc472f26 100644 --- a/lib/project/results-text-view-manager.js +++ b/lib/project/results-text-view-manager.js @@ -1,4 +1,5 @@ const { Range, CompositeDisposable } = require('atom'); +const {sanitizePattern} = require('./util'); const findGrammar = atom.grammars.createGrammar('find', { 'scopeName': 'find.results', @@ -87,6 +88,10 @@ class ResultsTextViewManager { // } onFinishedSearching(results) { + if (this.model.getPaths().length === 0) { + this.editor.setText(`No results found for ${sanitizePattern(results.findPattern)}.`); + return; + } this.editor.setGrammar(findGrammar); const searchContextLineCountTotal = this.searchContextLineCountBefore + this.searchContextLineCountAfter; let resultsLines = []; @@ -186,7 +191,7 @@ class ResultsTextViewManager { this.editor = editor; } - // Start a search in case there are new results + // Update the editor in case there are already results available this.onFinishedSearching(this.model.getResultsSummary()); return this.editor; } From ca5879c610883298ae913a1764d6259a09e3f280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 23 Jul 2017 17:52:35 +0200 Subject: [PATCH 14/17] Dynamic dots length for separator. Set cursor at the beginning after setting text. --- lib/project/results-text-view-manager.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js index bc472f26..4e93e41a 100644 --- a/lib/project/results-text-view-manager.js +++ b/lib/project/results-text-view-manager.js @@ -154,7 +154,7 @@ class ResultsTextViewManager { // Separator if (!linesOverlap) { - resultsLines.push(pad('..')); + resultsLines.push(pad('.'.repeat((mainLineNumber + numberOfTrailingLines).toString().length))); } lastLineNumber = mainLineNumber; @@ -164,6 +164,7 @@ class ResultsTextViewManager { resultsLines.push(''); }); this.editor.setText(resultsLines.join('\n')); + this.editor.setCursorBufferPosition([0, 0]); } getResultsTextEditor() { From 3a113e562df319329b1f9bb12d5c9bf7585a400c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 23 Jul 2017 18:12:03 +0200 Subject: [PATCH 15/17] Cleanup. Observe line count before and after from atom.config --- lib/find.coffee | 2 +- lib/project/results-text-view-manager.js | 40 ++++++++---------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/lib/find.coffee b/lib/find.coffee index 2049b857..f55917b0 100644 --- a/lib/find.coffee +++ b/lib/find.coffee @@ -20,7 +20,7 @@ module.exports = atom.workspace.addOpener (filePath) => if filePath is ResultsPaneView.URI - if atom.config.get('find-and-replace.openProjectFindResultsAsText') + if atom.config.get('find-and-replace.findResultsAsText') return @resultsTextViewManager.getResultsTextEditor() else return new ResultsPaneView() diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js index 4e93e41a..c8ecc979 100644 --- a/lib/project/results-text-view-manager.js +++ b/lib/project/results-text-view-manager.js @@ -41,10 +41,6 @@ class ResultsTextViewManager { this.cursorLine = null; this.lineToFilesMap = {}; this.isLoading = false; - // this.searchErrors = []; - // this.searchResults = null; - // this.searchingIsSlow = false; - // this.numberOfPathsSearched = 0; this.searchContextLineCountBefore = atom.config.get('find-and-replace.searchContextLineCountBefore'); this.searchContextLineCountAfter = atom.config.get('find-and-replace.searchContextLineCountAfter'); } @@ -72,20 +68,9 @@ class ResultsTextViewManager { this.editor = null; } - // onPathsSearched({result}) { - // this.editor.insertText result.filePath - // this.editor.insertNewline({autoIndentNewline: false}) - // result.matches.forEach (match) -> - // match.leadingContextLines.forEach (leadingContextLine) -> - // this.editor.insertText leadingContextLine - // this.editor.insertNewline({autoIndentNewline: false}) - // this.editor.insertText match.lineText - // this.editor.insertNewline({autoIndentNewline: false}) - // match.trailingContextLines.forEach (trailingContextLine) -> - // this.editor.insertText trailingContextLine - // this.editor.insertNewline({autoIndentNewline: false}) - // this.editor.insertNewline({autoIndentNewline: false}) - // } + onSearch() { + this.editor.setText('Searching...'); + } onFinishedSearching(results) { if (this.model.getPaths().length === 0) { @@ -164,7 +149,12 @@ class ResultsTextViewManager { resultsLines.push(''); }); this.editor.setText(resultsLines.join('\n')); - this.editor.setCursorBufferPosition([0, 0]); + this.editor.setCursorBufferPosition([0, 0]); + } + + onSearchContextLineCountChanged() { + this.searchContextLineCountBefore = atom.config.get('find-and-replace.searchContextLineCountBefore'); + this.searchContextLineCountAfter = atom.config.get('find-and-replace.searchContextLineCountAfter'); } getResultsTextEditor() { @@ -180,14 +170,10 @@ class ResultsTextViewManager { this.subscriptions = new CompositeDisposable( editor.onDidChangeCursorPosition(this.onCursorPositionChanged.bind(this)), editor.onDidDestroy(this.onDestroyEditor.bind(this)), - // this.model.onDidStartSearching(this.onSearch.bind(this)), - this.model.onDidFinishSearching(this.onFinishedSearching.bind(this)) - // this.model.onDidClear(this.onCleared.bind(this)), - // this.model.onDidClearReplacementState(this.onReplacementStateCleared.bind(this)), - // this.model.onDidSearchPaths(this.onPathsSearched.bind(this)), - // this.model.onDidErrorForPath(error => this.appendError(error.message)), - // atom.config.observe('find-and-replace.searchContextLineCountBefore', this.searchContextLineCountChanged.bind(this)), - // atom.config.observe('find-and-replace.searchContextLineCountAfter', this.searchContextLineCountChanged.bind(this)) + this.model.onDidStartSearching(this.onSearch.bind(this)), + this.model.onDidFinishSearching(this.onFinishedSearching.bind(this)), + atom.config.observe('find-and-replace.searchContextLineCountBefore', this.onSearchContextLineCountChanged.bind(this)), + atom.config.observe('find-and-replace.searchContextLineCountAfter', this.onSearchContextLineCountChanged.bind(this)) ); this.editor = editor; From aea5ba4c664dacf427e0c75ff1d5601936bcf7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Sun, 23 Jul 2017 20:01:27 +0200 Subject: [PATCH 16/17] Adding some basic tests --- lib/project/results-text-view-manager.js | 3 +- spec/results-text-view-manager-spec.js | 56 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 spec/results-text-view-manager-spec.js diff --git a/lib/project/results-text-view-manager.js b/lib/project/results-text-view-manager.js index c8ecc979..105a2b44 100644 --- a/lib/project/results-text-view-manager.js +++ b/lib/project/results-text-view-manager.js @@ -64,6 +64,7 @@ class ResultsTextViewManager { }; onDestroyEditor() { + this.model.setActive(false); this.subscriptions.dispose(); this.editor = null; } @@ -162,7 +163,7 @@ class ResultsTextViewManager { this.cursorLine = null; this.lineToFilesMap = {}; const textEditorRegistry = atom.workspace.textEditorRegistry; - const editor = textEditorRegistry.build(); + const editor = textEditorRegistry.build(({autoHeight: false})); editor.getTitle = () => 'Project Find Results'; editor.getIconName = () => 'search'; editor.shouldPromptToSave = () => false; diff --git a/spec/results-text-view-manager-spec.js b/spec/results-text-view-manager-spec.js new file mode 100644 index 00000000..d0e282fe --- /dev/null +++ b/spec/results-text-view-manager-spec.js @@ -0,0 +1,56 @@ +/** @babel */ +const path = require('path'); +const ResultsPaneView = require('../lib/project/results-pane'); +const {beforeEach, it, fit, ffit, fffit} = require('./async-spec-helpers') + +global.beforeEach(function() { + this.addMatchers({ + toBeWithin(value, delta) { + this.message = `Expected ${this.actual} to be within ${delta} of ${value}` + return Math.abs(this.actual - value) < delta; + } + }); +}); + +describe('ResultsTextViewManager', () => { + let projectFindView, resultsView, searchPromise, workspaceElement; + + function getResultsEditor() { + return atom.workspace.getActiveTextEditor(); + } + + beforeEach(async () => { + workspaceElement = atom.views.getView(atom.workspace); + jasmine.attachToDOM(workspaceElement); + + atom.config.set('core.excludeVcsIgnoredPaths', false); + atom.config.set('find-and-replace.findResultsAsText', true); + atom.project.setPaths([path.join(__dirname, 'fixtures')]); + + let activationPromise = atom.packages.activatePackage("find-and-replace").then(function({mainModule}) { + mainModule.createViews(); + ({projectFindView} = mainModule); + const spy = spyOn(projectFindView, 'confirm').andCallFake(() => { + return searchPromise = spy.originalValue.call(projectFindView) + }); + }); + + atom.commands.dispatch(workspaceElement, 'project-find:show'); + + await activationPromise; + }); + + describe("when the result is for a long line", () => { + it("renders just one line", async () => { + projectFindView.findEditor.setText('ghijkl'); + atom.commands.dispatch(projectFindView.element, 'core:confirm'); + await searchPromise; + + resultsEditor = getResultsEditor(); + resultsEditor.update({autoHeight: false}) + const lines = resultsEditor.getText().split('\n'); + expect(lines[0]).toBe("\x1C/Users/javi/Development/github/find-and-replace/spec/fixtures/one-long-line.coffee:\x1C"); + expect(lines[1]).toBe("\x1B 1:\x1B test test test test test test test test test test test a b c d e f g h i j k l abcdefghijklmnopqrstuvwxyz"); + }) + }); +}); From c50d28185615e653fb7686650008639d3cd6c640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Cha=CC=81varri?= Date: Mon, 24 Jul 2017 01:12:18 +0200 Subject: [PATCH 17/17] Fixing tests (remove file paths) --- spec/results-text-view-manager-spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/results-text-view-manager-spec.js b/spec/results-text-view-manager-spec.js index d0e282fe..4e281390 100644 --- a/spec/results-text-view-manager-spec.js +++ b/spec/results-text-view-manager-spec.js @@ -49,7 +49,6 @@ describe('ResultsTextViewManager', () => { resultsEditor = getResultsEditor(); resultsEditor.update({autoHeight: false}) const lines = resultsEditor.getText().split('\n'); - expect(lines[0]).toBe("\x1C/Users/javi/Development/github/find-and-replace/spec/fixtures/one-long-line.coffee:\x1C"); expect(lines[1]).toBe("\x1B 1:\x1B test test test test test test test test test test test a b c d e f g h i j k l abcdefghijklmnopqrstuvwxyz"); }) });