default-directory-searcher.js
1 const Task = require('./task'); 2 3 // Searches local files for lines matching a specified regex. Implements `.then()` 4 // so that it can be used with `Promise.all()`. 5 class DirectorySearch { 6 constructor(rootPaths, regex, options) { 7 const scanHandlerOptions = { 8 ignoreCase: regex.ignoreCase, 9 inclusions: options.inclusions, 10 includeHidden: options.includeHidden, 11 excludeVcsIgnores: options.excludeVcsIgnores, 12 globalExclusions: options.exclusions, 13 follow: options.follow 14 }; 15 const searchOptions = { 16 leadingContextLineCount: options.leadingContextLineCount, 17 trailingContextLineCount: options.trailingContextLineCount 18 }; 19 this.task = new Task(require.resolve('./scan-handler')); 20 this.task.on('scan:result-found', options.didMatch); 21 this.task.on('scan:file-error', options.didError); 22 this.task.on('scan:paths-searched', options.didSearchPaths); 23 this.promise = new Promise((resolve, reject) => { 24 this.task.on('task:cancelled', reject); 25 this.task.start( 26 rootPaths, 27 regex.source, 28 scanHandlerOptions, 29 searchOptions, 30 () => { 31 this.task.terminate(); 32 resolve(); 33 } 34 ); 35 }); 36 } 37 38 then(...args) { 39 return this.promise.then.apply(this.promise, args); 40 } 41 42 cancel() { 43 // This will cause @promise to reject. 44 this.task.cancel(); 45 } 46 } 47 48 // Default provider for the `atom.directory-searcher` service. 49 module.exports = class DefaultDirectorySearcher { 50 // Determines whether this object supports search for a `Directory`. 51 // 52 // * `directory` {Directory} whose search needs might be supported by this object. 53 // 54 // Returns a `boolean` indicating whether this object can search this `Directory`. 55 canSearchDirectory(directory) { 56 return true; 57 } 58 59 // Performs a text search for files in the specified `Directory`, subject to the 60 // specified parameters. 61 // 62 // Results are streamed back to the caller by invoking methods on the specified `options`, 63 // such as `didMatch` and `didError`. 64 // 65 // * `directories` {Array} of {Directory} objects to search, all of which have been accepted by 66 // this searcher's `canSearchDirectory()` predicate. 67 // * `regex` {RegExp} to search with. 68 // * `options` {Object} with the following properties: 69 // * `didMatch` {Function} call with a search result structured as follows: 70 // * `searchResult` {Object} with the following keys: 71 // * `filePath` {String} absolute path to the matching file. 72 // * `matches` {Array} with object elements with the following keys: 73 // * `lineText` {String} The full text of the matching line (without a line terminator character). 74 // * `lineTextOffset` {Number} If > 0, the provided line text is truncated and starts at this offset 75 // * `matchText` {String} The text that matched the `regex` used for the search. 76 // * `range` {Range} Identifies the matching region in the file. (Likely as an array of numeric arrays.) 77 // * `didError` {Function} call with an Error if there is a problem during the search. 78 // * `didSearchPaths` {Function} periodically call with the number of paths searched thus far. 79 // * `inclusions` {Array} of glob patterns (as strings) to search within. Note that this 80 // array may be empty, indicating that all files should be searched. 81 // 82 // Each item in the array is a file/directory pattern, e.g., `src` to search in the "src" 83 // directory or `*.js` to search all JavaScript files. In practice, this often comes from the 84 // comma-delimited list of patterns in the bottom text input of the ProjectFindView dialog. 85 // * `includeHidden` {boolean} whether to ignore hidden files. 86 // * `excludeVcsIgnores` {boolean} whether to exclude VCS ignored paths. 87 // * `exclusions` {Array} similar to inclusions 88 // * `follow` {boolean} whether symlinks should be followed. 89 // 90 // Returns a *thenable* `DirectorySearch` that includes a `cancel()` method. If `cancel()` is 91 // invoked before the `DirectorySearch` is determined, it will resolve the `DirectorySearch`. 92 search(directories, regex, options) { 93 const rootPaths = directories.map(directory => directory.getPath()); 94 let isCancelled = false; 95 const directorySearch = new DirectorySearch(rootPaths, regex, options); 96 const promise = new Promise(function(resolve, reject) { 97 directorySearch.then(resolve, function() { 98 if (isCancelled) { 99 resolve(); 100 } else { 101 reject(); // eslint-disable-line prefer-promise-reject-errors 102 } 103 }); 104 }); 105 return { 106 then: promise.then.bind(promise), 107 catch: promise.catch.bind(promise), 108 cancel() { 109 isCancelled = true; 110 directorySearch.cancel(); 111 } 112 }; 113 } 114 };