This repository was archived by the owner on Dec 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 193
Search results as text #933
Open
Open
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
0ee834f
First attempts
jchavarri 18ffbd6
Moving logic to results-text-builder. Bridging resultsModel to text e...
jchavarri 0cb3314
Adding line numbers + some basic styles / grammar
jchavarri 2776c93
Add left pad, keep same tab, group close results
jchavarri 5df7993
Cleanup
jchavarri d8da578
Some basic "double click to open file" support
jchavarri 94e6ae4
Different styles between main line and context lines
jchavarri 69c7a8a
Create ResultsTextViewManager
jchavarri 29e745a
Remove unneeded removeEventListener
jchavarri 34e6f47
Using Range.fromObject
jchavarri 78c451d
Open text results depending on config
jchavarri 0391f49
Read # lines before and after from atom.config
jchavarri dbda9a9
Add βno resultsβ text
jchavarri ca5879c
Dynamic dots length for separator. Set cursor at the beginning after ...
jchavarri 3a113e5
Cleanup. Observe line count before and after from atom.config
jchavarri aea5ba4
Adding some basic tests
jchavarri c50d281
Fixing tests (remove file paths)
jchavarri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
186 changes: 186 additions & 0 deletions
lib/project/results-text-view-manager.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,186 @@ | ||
| const { Range, CompositeDisposable } = require('atom'); | ||
| const {sanitizePattern} = require('./util'); | ||
|
|
||
| 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.searchContextLineCountBefore = atom.config.get('find-and-replace.searchContextLineCountBefore'); | ||
| this.searchContextLineCountAfter = atom.config.get('find-and-replace.searchContextLineCountAfter'); | ||
| } | ||
|
|
||
| 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() { | ||
| this.model.setActive(false); | ||
| this.subscriptions.dispose(); | ||
| this.editor = null; | ||
| } | ||
|
|
||
| onSearch() { | ||
| this.editor.setText('Searching...'); | ||
| } | ||
|
|
||
| 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 = []; | ||
| this.model.getPaths().forEach((filePath) => { | ||
| const result = this.model.results[filePath]; | ||
| resultsLines.push('\x1C' + filePath + ':\x1C'); | ||
| let lastLineNumber = null; | ||
| for (let i = 0; i < result.matches.length; i++) { | ||
| const match = result.matches[i]; | ||
| const mainLineNumber = Range.fromObject(match.range).start.row + 1; | ||
|
|
||
| // 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]); | ||
| }; | ||
|
|
||
| // Avoid adding the same line multiple times | ||
| if (mainLineNumber !== lastLineNumber) { | ||
| // 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 | ||
| for (let j = 0; j < numberOfTrailingLines; j++) { | ||
| const lineNumber = mainLineNumber + j + 1; | ||
| resultsLines.push('\x1D' + pad(lineNumber) + '\x1D ' + match.trailingContextLines[j]); | ||
| }; | ||
|
|
||
| // Separator | ||
| if (!linesOverlap) { | ||
| resultsLines.push(pad('.'.repeat((mainLineNumber + numberOfTrailingLines).toString().length))); | ||
| } | ||
|
|
||
| lastLineNumber = mainLineNumber; | ||
| }; | ||
| // Pop last separator | ||
| resultsLines.pop(); | ||
| resultsLines.push(''); | ||
| }); | ||
| this.editor.setText(resultsLines.join('\n')); | ||
| 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() { | ||
| if (!this.editor) { | ||
| this.cursorLine = null; | ||
| this.lineToFilesMap = {}; | ||
| const textEditorRegistry = atom.workspace.textEditorRegistry; | ||
| const editor = textEditorRegistry.build(({autoHeight: false})); | ||
| editor.getTitle = () => 'Project Find Results'; | ||
| editor.getIconName = () => 'search'; | ||
| editor.shouldPromptToSave = () => false; | ||
| editor.element.addEventListener('dblclick', this.onDoubleClick.bind(this)); | ||
| 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)), | ||
| 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; | ||
| } | ||
| // Update the editor in case there are already results available | ||
| this.onFinishedSearching(this.model.getResultsSummary()); | ||
| return this.editor; | ||
| } | ||
| } |
55 changes: 55 additions & 0 deletions
spec/results-text-view-manager-spec.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /** @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[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"); | ||
| }) | ||
| }); | ||
| }); |
18 changes: 18 additions & 0 deletions
styles/find-and-replace.atom-text-editor.less
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // 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-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-import; | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.