From 239cba25e462df65535d5bf183e9267907a96e67 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 19 Jul 2012 17:28:46 -0600 Subject: [PATCH] Use the silver searcher to implement global search Parsing uses a simple state machine in `Project.scan`. --- spec/app/project-spec.coffee | 8 ++--- src/app/project.coffee | 64 ++++++++++++++++++++++++----------- vendor/ag | Bin 0 -> 38956 bytes 3 files changed, 48 insertions(+), 24 deletions(-) create mode 100755 vendor/ag diff --git a/spec/app/project-spec.coffee b/spec/app/project-spec.coffee index ae0232e1c..a8a50a0cc 100644 --- a/spec/app/project-spec.coffee +++ b/spec/app/project-spec.coffee @@ -128,12 +128,12 @@ describe "Project", -> runs -> expect(matches[0]).toEqual path: project.resolve('a') - match: ['aaa', 'a'] + match: 'aaa' range: [[0, 0], [0, 3]] expect(matches[1]).toEqual path: project.resolve('a') - match: ['aa', 'a'] + match: 'aa' range: [[1, 3], [1, 5]] it "works on evil filenames", -> @@ -147,11 +147,9 @@ describe "Project", -> runs -> expect(paths.length).toBe 5 - matches.forEach (match) -> expect(match).toEqual ['evil'] + matches.forEach (match) -> expect(match).toEqual 'evil' expect(paths[0]).toMatch /a_file_with_utf8.txt$/ expect(paths[1]).toMatch /file with spaces.txt$/ expect(paths[2]).toMatch /goddam\nnewlines$/m expect(paths[3]).toMatch /quote".txt$/m expect(fs.base(paths[4])).toBe "utfa\u0306.md" - - diff --git a/src/app/project.coffee b/src/app/project.coffee index 918de78e6..72745f1e9 100644 --- a/src/app/project.coffee +++ b/src/app/project.coffee @@ -138,32 +138,58 @@ class Project scan: (regex, iterator) -> regex = new RegExp(regex.source, 'g') - command = "#{require.resolve('ack')} --all --match \"#{regex.source}\" \"#{@getPath()}\"" + command = "#{require.resolve('ag')} --ackmate \"#{regex.source}\" \"#{@getPath()}\"" bufferedData = "" - console.log command - promise = ChildProcess.exec command , bufferLines: true, stdout: (data) -> - bufferedData += data - currentIndex = 0 - while currentIndex < bufferedData.length - pathEndIndex = bufferedData.indexOf('\0', currentIndex) - break unless pathEndIndex >= 0 - lineNumberEndIndex = bufferedData.indexOf('\0', pathEndIndex + 1) - lineEndIndex = bufferedData.indexOf('\n', lineNumberEndIndex + 1) + state = 'readingPath' + path = null + + readPath = (line) -> + if /^[0-9,; ]+:/.test(line) + state = 'readingLines' + else if /^:/.test line + path = line.substr(1) + else + path += ('\n' + line) + + readLine = (line) -> + if line.length == 0 + state = 'readingPath' + path = null + else + colonIndex = line.indexOf(':') + matchInfo = line.substring(0, colonIndex) + lineText = line.substring(colonIndex + 1) + readMatches(matchInfo, lineText) + + readMatches = (matchInfo, lineText) -> + [lineNumber, matchPositionsText] = matchInfo.match(/(\d+);(.+)/)[1..] + row = parseInt(lineNumber) - 1 + + + + matchPositions = matchPositionsText.split(',').map (positionText) -> positionText.split(' ').map (pos) -> parseInt(pos) + + for [column, length] in matchPositions + range = new Range([row, column], [row, column + length]) + match = lineText.substr(column, length) + iterator({path, range, match}) + + ChildProcess.exec command , bufferLines: true, stdout: (data) -> + lines = data.split('\n') + try + for line in lines + readPath(line) if state is 'readingPath' + readLine(line) if state is 'readingLines' + catch e + console.log e.stack + + - path = bufferedData.substring(currentIndex, pathEndIndex) - row = parseInt(bufferedData.substring(pathEndIndex + 1, lineNumberEndIndex)) - 1 - line = bufferedData.substring(lineNumberEndIndex + 1, lineEndIndex) - while match = regex.exec(line) - range = new Range([row, match.index], [row, match.index + match[0].length]) - iterator({path, match, range}) - currentIndex = lineEndIndex + 1 - bufferedData = bufferedData.substring(currentIndex) - promise.done -> console.log "DONE" _.extend Project.prototype, EventEmitter diff --git a/vendor/ag b/vendor/ag new file mode 100755 index 0000000000000000000000000000000000000000..9fe011bbdc9cac890d40933136fcfe838c6e02bf GIT binary patch literal 38956 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