Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Search results as text #933

Open
jchavarri wants to merge 17 commits into atom:master
base: master
Choose a base branch
Loading
from jchavarri:search-results-as-text
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
0ee834f
First attempts
jchavarri Jul 9, 2017
18ffbd6
Moving logic to results-text-builder. Bridging resultsModel to text e...
jchavarri Jul 9, 2017
0cb3314
Adding line numbers + some basic styles / grammar
jchavarri Jul 10, 2017
2776c93
Add left pad, keep same tab, group close results
jchavarri Jul 15, 2017
5df7993
Cleanup
jchavarri Jul 15, 2017
d8da578
Some basic "double click to open file" support
jchavarri Jul 16, 2017
94e6ae4
Different styles between main line and context lines
jchavarri Jul 16, 2017
69c7a8a
Create ResultsTextViewManager
jchavarri Jul 16, 2017
29e745a
Remove unneeded removeEventListener
jchavarri Jul 17, 2017
34e6f47
Using Range.fromObject
jchavarri Jul 21, 2017
78c451d
Open text results depending on config
jchavarri Jul 22, 2017
0391f49
Read # lines before and after from atom.config
jchavarri Jul 23, 2017
dbda9a9
Add β€˜no results’ text
jchavarri Jul 23, 2017
ca5879c
Dynamic dots length for separator. Set cursor at the beginning after ...
jchavarri Jul 23, 2017
3a113e5
Cleanup. Observe line count before and after from atom.config
jchavarri Jul 23, 2017
aea5ba4
Adding some basic tests
jchavarri Jul 23, 2017
c50d281
Fixing tests (remove file paths)
jchavarri Jul 23, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions lib/find.coffee
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ FindView = require './find-view'
ProjectFindView = require './project-find-view'
ResultsModel = require './project/results-model'
ResultsPaneView = require './project/results-pane'
ResultsTextViewManager = require './project/results-text-view-manager'

module.exports =
activate: ({findOptions, findHistory, replaceHistory, pathsHistory}={}) ->
Expand All @@ -17,8 +18,12 @@ 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
if atom.config.get('find-and-replace.findResultsAsText')
return @resultsTextViewManager.getResultsTextEditor()
else
return new ResultsPaneView()

@subscriptions = new CompositeDisposable
@findHistory = new History(findHistory)
Expand All @@ -29,6 +34,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)
Expand Down Expand Up @@ -174,6 +181,7 @@ module.exports =
@projectFindView = null

ResultsPaneView.model = null
ResultsTextViewManager.model = null
@resultsModel = null

@subscriptions?.dispose()
Expand Down
186 changes: 186 additions & 0 deletions lib/project/results-text-view-manager.js
View file Open in desktop
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
View file Open in desktop
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
View file Open in desktop
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;
}

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /