diff options
| author | Ben Beltran <ben@nsovocal.com> | 2015-07-10 11:12:25 -0500 |
|---|---|---|
| committer | Ben Beltran <ben@nsovocal.com> | 2015-07-10 11:12:25 -0500 |
| commit | 24c7594d62d8d7fbbcdb64b11ce4adc5d8e6991a (patch) | |
| tree | ded312222bb108923da1820ba40b04d710d20e5b /atom/packages/vim-mode/lib/motions/search-motion.coffee | |
| parent | eb786e82d170e2abc351a432ade616d6ecdeeb6b (diff) | |
Adds atom
Diffstat (limited to 'atom/packages/vim-mode/lib/motions/search-motion.coffee')
| -rw-r--r-- | atom/packages/vim-mode/lib/motions/search-motion.coffee | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/atom/packages/vim-mode/lib/motions/search-motion.coffee b/atom/packages/vim-mode/lib/motions/search-motion.coffee new file mode 100644 index 0000000..11761ad --- /dev/null +++ b/atom/packages/vim-mode/lib/motions/search-motion.coffee @@ -0,0 +1,210 @@ +_ = require 'underscore-plus' +{MotionWithInput} = require './general-motions' +SearchViewModel = require '../view-models/search-view-model' +{Input} = require '../view-models/view-model' +{Point, Range} = require 'atom' +settings = require '../settings' + +class SearchBase extends MotionWithInput + operatesInclusively: false + + constructor: (@editor, @vimState, options = {}) -> + super(@editor, @vimState) + @reverse = @initiallyReversed = false + @updateCurrentSearch() unless options.dontUpdateCurrentSearch + + reversed: => + @initiallyReversed = @reverse = true + @updateCurrentSearch() + this + + moveCursor: (cursor, count=1) -> + ranges = @scan(cursor) + if ranges.length > 0 + range = ranges[(count - 1) % ranges.length] + cursor.setBufferPosition(range.start) + else + atom.beep() + + scan: (cursor) -> + currentPosition = cursor.getBufferPosition() + + [rangesBefore, rangesAfter] = [[], []] + @editor.scan @getSearchTerm(@input.characters), ({range}) => + isBefore = if @reverse + range.start.compare(currentPosition) < 0 + else + range.start.compare(currentPosition) <= 0 + + if isBefore + rangesBefore.push(range) + else + rangesAfter.push(range) + + if @reverse + rangesAfter.concat(rangesBefore).reverse() + else + rangesAfter.concat(rangesBefore) + + getSearchTerm: (term) -> + modifiers = {'g': true} + + if not term.match('[A-Z]') and settings.useSmartcaseForSearch() + modifiers['i'] = true + + if term.indexOf('\\c') >= 0 + term = term.replace('\\c', '') + modifiers['i'] = true + + modFlags = Object.keys(modifiers).join('') + + try + new RegExp(term, modFlags) + catch + new RegExp(_.escapeRegExp(term), modFlags) + + updateCurrentSearch: -> + @vimState.globalVimState.currentSearch.reverse = @reverse + @vimState.globalVimState.currentSearch.initiallyReversed = @initiallyReversed + + replicateCurrentSearch: -> + @reverse = @vimState.globalVimState.currentSearch.reverse + @initiallyReversed = @vimState.globalVimState.currentSearch.initiallyReversed + +class Search extends SearchBase + constructor: (@editor, @vimState) -> + super(@editor, @vimState) + @viewModel = new SearchViewModel(this) + +class SearchCurrentWord extends SearchBase + @keywordRegex: null + + constructor: (@editor, @vimState) -> + super(@editor, @vimState) + + # FIXME: This must depend on the current language + defaultIsKeyword = "[@a-zA-Z0-9_\-]+" + userIsKeyword = atom.config.get('vim-mode.iskeyword') + @keywordRegex = new RegExp(userIsKeyword or defaultIsKeyword) + + searchString = @getCurrentWordMatch() + @input = new Input(searchString) + @vimState.pushSearchHistory(searchString) unless searchString is @vimState.getSearchHistoryItem() + + getCurrentWord: -> + cursor = @editor.getLastCursor() + wordStart = cursor.getBeginningOfCurrentWordBufferPosition(wordRegex: @keywordRegex, allowPrevious: false) + wordEnd = cursor.getEndOfCurrentWordBufferPosition (wordRegex: @keywordRegex, allowNext: false) + cursorPosition = cursor.getBufferPosition() + + if wordEnd.column is cursorPosition.column + # either we don't have a current word, or it ends on cursor, i.e. precedes it, so look for the next one + wordEnd = cursor.getEndOfCurrentWordBufferPosition (wordRegex: @keywordRegex, allowNext: true) + return "" if wordEnd.row isnt cursorPosition.row # don't look beyond the current line + + cursor.setBufferPosition wordEnd + wordStart = cursor.getBeginningOfCurrentWordBufferPosition(wordRegex: @keywordRegex, allowPrevious: false) + + cursor.setBufferPosition wordStart + + @editor.getTextInBufferRange([wordStart, wordEnd]) + + cursorIsOnEOF: (cursor) -> + pos = cursor.getNextWordBoundaryBufferPosition(wordRegex: @keywordRegex) + eofPos = @editor.getEofBufferPosition() + pos.row is eofPos.row and pos.column is eofPos.column + + getCurrentWordMatch: -> + characters = @getCurrentWord() + if characters.length > 0 + if /\W/.test(characters) then "#{characters}\\b" else "\\b#{characters}\\b" + else + characters + + isComplete: -> true + + execute: (count=1) -> + super(count) if @input.characters.length > 0 + +OpenBrackets = ['(', '{', '['] +CloseBrackets = [')', '}', ']'] +AnyBracket = new RegExp(OpenBrackets.concat(CloseBrackets).map(_.escapeRegExp).join("|")) + +class BracketMatchingMotion extends SearchBase + operatesInclusively: true + + isComplete: -> true + + searchForMatch: (startPosition, reverse, inCharacter, outCharacter) -> + depth = 0 + point = startPosition.copy() + lineLength = @editor.lineTextForBufferRow(point.row).length + eofPosition = @editor.getEofBufferPosition().translate([0, 1]) + increment = if reverse then -1 else 1 + + loop + character = @characterAt(point) + depth++ if character is inCharacter + depth-- if character is outCharacter + + return point if depth is 0 + + point.column += increment + + return null if depth < 0 + return null if point.isEqual([0, -1]) + return null if point.isEqual(eofPosition) + + if point.column < 0 + point.row-- + lineLength = @editor.lineTextForBufferRow(point.row).length + point.column = lineLength - 1 + else if point.column >= lineLength + point.row++ + lineLength = @editor.lineTextForBufferRow(point.row).length + point.column = 0 + + characterAt: (position) -> + @editor.getTextInBufferRange([position, position.translate([0, 1])]) + + getSearchData: (position) -> + character = @characterAt(position) + if (index = OpenBrackets.indexOf(character)) >= 0 + [character, CloseBrackets[index], false] + else if (index = CloseBrackets.indexOf(character)) >= 0 + [character, OpenBrackets[index], true] + else + [] + + moveCursor: (cursor) -> + startPosition = cursor.getBufferPosition() + + [inCharacter, outCharacter, reverse] = @getSearchData(startPosition) + + unless inCharacter? + restOfLine = [startPosition, [startPosition.row, Infinity]] + @editor.scanInBufferRange AnyBracket, restOfLine, ({range, stop}) -> + startPosition = range.start + stop() + + [inCharacter, outCharacter, reverse] = @getSearchData(startPosition) + + return unless inCharacter? + + if matchPosition = @searchForMatch(startPosition, reverse, inCharacter, outCharacter) + cursor.setBufferPosition(matchPosition) + +class RepeatSearch extends SearchBase + constructor: (@editor, @vimState) -> + super(@editor, @vimState, dontUpdateCurrentSearch: true) + @input = new Input(@vimState.getSearchHistoryItem(0) ? "") + @replicateCurrentSearch() + + isComplete: -> true + + reversed: -> + @reverse = not @initiallyReversed + this + + +module.exports = {Search, SearchCurrentWord, BracketMatchingMotion, RepeatSearch} |