diff --git a/lib/command.coffee b/lib/command.coffee index ffb9b28..48e9616 100644 --- a/lib/command.coffee +++ b/lib/command.coffee @@ -8,14 +8,15 @@ class Command @selections = @exState.getSelections() @viewModel = new ExViewModel(@, Object.keys(@selections).length > 0) - parseAddr: (str, curPos) -> + parseAddr: (str, cursor) -> + row = cursor.getBufferRow() if str is '.' - addr = curPos.row + addr = row else if str is '$' # Lines are 0-indexed in Atom, but 1-indexed in vim. addr = @editor.getBuffer().lines.length - 1 else if str[0] in ["+", "-"] - addr = curPos.row + @parseOffset(str) + addr = row + @parseOffset(str) else if not isNaN(str) addr = parseInt(str) - 1 else if str[0] is "'" # Parse Mark... @@ -26,13 +27,21 @@ class Command throw new CommandError("Mark #{str} not set.") addr = mark.getEndBufferPosition().row else if str[0] is "/" - addr = Find.findNextInBuffer(@editor.buffer, curPos, str[1...-1]) + str = str[1...] + if str[str.length-1] is "/" + str = str[...-1] + addr = Find.scanEditor(str, @editor, cursor.getCurrentLineBufferRange().end)[0] unless addr? - throw new CommandError("Pattern not found: #{str[1...-1]}") + throw new CommandError("Pattern not found: #{str}") + addr = addr.start.row else if str[0] is "?" - addr = Find.findPreviousInBuffer(@editor.buffer, curPos, str[1...-1]) + str = str[1...] + if str[str.length-1] is "?" + str = str[...-1] + addr = Find.scanEditor(str, @editor, cursor.getCurrentLineBufferRange().start, true)[0] unless addr? throw new CommandError("Pattern not found: #{str[1...-1]}") + addr = addr.start.row return addr @@ -76,8 +85,8 @@ class Command \$| # Last line \d+| # n-th line '[\[\]<>'`"^.(){}a-zA-Z]| # Marks - /.*?[^\\]/| # Regex - \?.*?[^\\]\?| # Backwards search + /.*?(?:[^\\]/|$)| # Regex + \?.*?(?:[^\\]\?|$)| # Backwards search [+-]\d* # Current line +/- a number of lines )((?:\s*[+-]\d*)*) # Line offset )? @@ -96,7 +105,7 @@ class Command [match, addr1, off1, addr2, off2] = cl.match(addrPattern) - curPos = @editor.getCursorBufferPosition() + cursor = @editor.getLastCursor() # Special case: run command on selection. This can't be handled by simply # parsing the mark since vim-mode doesn't set it (and it would be fairly @@ -106,10 +115,10 @@ class Command else runOverSelections = false if addr1? - address1 = @parseAddr(addr1, curPos) + address1 = @parseAddr(addr1, cursor) else # If no addr1 is given (,+3), assume it is '.' - address1 = curPos.row + address1 = cursor.getBufferRow() if off1? address1 += @parseOffset(off1) @@ -119,7 +128,7 @@ class Command throw new CommandError('Invalid range') if addr2? - address2 = @parseAddr(addr2, curPos) + address2 = @parseAddr(addr2, cursor) if off2? address2 += @parseOffset(off2) diff --git a/lib/find.coffee b/lib/find.coffee index 53dffc5..60e98b8 100644 --- a/lib/find.coffee +++ b/lib/find.coffee @@ -1,3 +1,40 @@ +_ = require 'underscore-plus' + +getSearchTerm = (term, modifiers = {'g': true}) -> + + escaped = false + hasc = false + hasC = false + term_ = term + term = '' + for char in term_ + if char is '\\' and not escaped + escaped = true + term += char + else + if char is 'c' and escaped + hasc = true + term = term[...-1] + else if char is 'C' and escaped + hasC = true + term = term[...-1] + else if char isnt '\\' + term += char + escaped = false + + if hasC + modifiers['i'] = false + if (not hasC and not term.match('[A-Z]') and \ + atom.config.get("vim-mode:useSmartcaseForSearch")) or hasc + modifiers['i'] = true + + modFlags = Object.keys(modifiers).filter((key) -> modifiers[key]).join('') + + try + new RegExp(term, modFlags) + catch + new RegExp(_.escapeRegExp(term), modFlags) + module.exports = { findInBuffer : (buffer, pattern) -> found = [] @@ -23,4 +60,26 @@ module.exports = { return found[found.length - 1].start.row else return null + + # Returns an array of ranges of all occurences of `term` in `editor`. + # The array is sorted so that the first occurences after the cursor come + # first (and the search wraps around). If `reverse` is true, the array is + # reversed so that the first occurence before the cursor comes first. + scanEditor: (term, editor, position, reverse = false) -> + [rangesBefore, rangesAfter] = [[], []] + editor.scan getSearchTerm(term), ({range}) -> + if reverse + isBefore = range.start.compare(position) < 0 + else + isBefore = range.start.compare(position) <= 0 + + if isBefore + rangesBefore.push(range) + else + rangesAfter.push(range) + + if reverse + rangesAfter.concat(rangesBefore).reverse() + else + rangesAfter.concat(rangesBefore) } diff --git a/spec/ex-commands-spec.coffee b/spec/ex-commands-spec.coffee index 311f18b..bfee143 100644 --- a/spec/ex-commands-spec.coffee +++ b/spec/ex-commands-spec.coffee @@ -118,9 +118,10 @@ describe "the commands", -> submitNormalModeInputText '/def' expect(editor.getCursorBufferPosition()).toEqual [1, 0] + editor.setCursorBufferPosition([2, 0]) openEx() - submitNormalModeInputText '?abc' - expect(editor.getCursorBufferPosition()).toEqual [0, 0] + submitNormalModeInputText '?def' + expect(editor.getCursorBufferPosition()).toEqual [1, 0] editor.setCursorBufferPosition([3, 0]) openEx()