diff --git a/lib/project-find-view.js b/lib/project-find-view.js index 3c98be87..88694a1d 100644 --- a/lib/project-find-view.js +++ b/lib/project-find-view.js @@ -178,6 +178,10 @@ class ProjectFindView { 'find-and-replace:use-selection-as-find-pattern': () => this.setSelectionAsFindPattern() })); + this.subscriptions.add(atom.commands.add('.preview-pane', { + 'project-find:copy-search-results': this.copySearchResultFromPane + })); + this.subscriptions.add(atom.commands.add(this.element, { 'find-and-replace:focus-next': () => this.focusNextElement(1), 'find-and-replace:focus-previous': () => this.focusNextElement(-1), @@ -188,7 +192,8 @@ class ProjectFindView { 'project-find:toggle-regex-option': () => this.toggleRegexOption(), 'project-find:toggle-case-option': () => this.toggleCaseOption(), 'project-find:toggle-whole-word-option': () => this.toggleWholeWordOption(), - 'project-find:replace-all': () => this.replaceAll() + 'project-find:replace-all': () => this.replaceAll(), + 'project-find:copy-search-results': () => this.copySearchResultFromPane() })); let updateInterfaceForSearching = () => { @@ -235,6 +240,11 @@ class ProjectFindView { this.handleEventsForReplace(); } + copySearchResultFromPane() { + atom.clipboard.write(Util.parseSearchResult()); + atom.notifications.addInfo('Search results have been copied to clipboard'); + } + handleEventsForReplace() { this.replaceEditor.getBuffer().onDidChange(() => this.model.clearReplacementState()); this.replaceEditor.onDidStopChanging(() => this.model.getFindOptions().set({replacePattern: this.replaceEditor.getText()})); diff --git a/lib/project/util.coffee b/lib/project/util.coffee index 6798e3b8..7dc308f1 100644 --- a/lib/project/util.coffee +++ b/lib/project/util.coffee @@ -36,7 +36,29 @@ showIf = (condition) -> else {display: 'none'} +parseSearchResult = -> + searchResult = [] + summary = document.querySelector('span.preview-count').textContent + searchResult.push summary, '' + + orderList = document.querySelectorAll('.results-view ol.list-tree.has-collapsable-children') + orderListArray = Array.prototype.slice.call(orderList) # only visible item shown in DOM, you cannot query all search results + resItems = orderListArray[1].querySelectorAll('div > li') # omit first element which is dummy + + resItems.forEach (el) -> + path = el.querySelector('div > span.path-name').textContent + matches = el.querySelector('div > span.path-match-number').textContent + searchResult.push "#{path} #{matches}" + + el.querySelectorAll('li.search-result').forEach (e) -> + return if e.offsetParent is null # skip invisible elements + lineNumber = e.querySelector('span.line-number').textContent + preview = e.querySelector('span.preview').textContent + searchResult.push "\t#{lineNumber}\t#{preview}" + searchResult.push '' + searchResult.join('\n') + module.exports = { escapeHtml, escapeRegex, sanitizePattern, getReplacementResultsMessage, - getSearchResultsMessage, showIf + getSearchResultsMessage, showIf, parseSearchResult } diff --git a/menus/find-and-replace.cson b/menus/find-and-replace.cson index e459ed2e..503020e5 100644 --- a/menus/find-and-replace.cson +++ b/menus/find-and-replace.cson @@ -28,3 +28,6 @@ '.path-details.list-item': [ { 'label': 'Copy Path', 'command': 'find-and-replace:copy-path' } ] + 'div.preview-pane': [ + { 'label': 'Copy to Clipboard', 'command': 'project-find:copy-search-results' } + ] diff --git a/spec/project-find-view-spec.js b/spec/project-find-view-spec.js index 11a6b26b..6d90f27a 100644 --- a/spec/project-find-view-spec.js +++ b/spec/project-find-view-spec.js @@ -724,6 +724,24 @@ describe('ProjectFindView', () => { expect(projectFindView.errorMessages).not.toBeVisible(); }); + it("can copy search results to clipboard", async () => { + oldClipboardText = atom.clipboard.read(); + + atom.commands.dispatch(projectFindView.element, 'core:confirm'); + await searchPromise; + + const resultsView = getResultsView(); + await resultsView.heightInvalidationPromise; + + atom.commands.dispatch(resultsView.element, 'project-find:copy-search-results'); + searchResults = atom.clipboard.read().split('\n'); + expect(searchResults[0]).toBe("13 results found in 2 files for items"); + expect(searchResults[2]).toBe("sample.coffee (7 matches)"); + expect(searchResults[3]).toBe("\t2\tsort: (items) ->"); + expect(searchResults[4]).toBe("\t3\treturn items if items.length <= 1"); + atom.clipboard.write(oldClipboardText); + }); + it("only searches paths matching text in the path filter", async () => { spyOn(atom.workspace, 'scan').andCallFake(async () => {}); projectFindView.pathsEditor.setText('*.js'); @@ -1508,4 +1526,4 @@ function simulateResizeEvent(element) { child.dispatchEvent(new AnimationEvent('animationstart')); }); advanceClock(1); -} \ No newline at end of file +}