laughing-hipster/.atom/packages/vim-mode/lib/motions/search-motion.coffee

280 lines
7.9 KiB
CoffeeScript

_ = require 'underscore-plus'
{MotionWithInput} = require './general-motions'
SearchViewModel = require '../view-models/search-view-model'
{Input} = require '../view-models/view-model'
{$$, Point, Range} = require 'atom'
class SearchBase extends MotionWithInput
@currentSearch: null
constructor: (@editorView, @vimState) ->
super(@editorView, @vimState)
Search.currentSearch = @
@reverse = @initiallyReversed = false
repeat: (opts = {}) =>
reverse = opts.backwards
if @initiallyReversed and reverse
@reverse = false
else
@reverse = reverse or @initiallyReversed
@
reversed: =>
@initiallyReversed = @reverse = true
@
execute: (count=1) ->
@scan()
@match count, (pos) =>
@editor.setCursorBufferPosition(pos.range.start)
select: (count=1) ->
@scan()
selectionStart = @getSelectionStart()
@match count, (pos) =>
reversed = selectionStart.compare(pos.range.start) > 0
@editor.setSelectedBufferRange([selectionStart, pos.range.start], {reversed})
[true]
getSelectionStart: ->
cur = @editor.getCursorBufferPosition()
{start, end} = @editor.getSelectedBufferRange()
if start.compare(cur) is 0 then end else start
match: (count, callback) ->
pos = @matches[(count - 1) % @matches.length]
if pos?
callback(pos)
else
atom.beep()
scan: ->
term = @input.characters
regexp =
try
new RegExp(term, 'g')
catch
new RegExp(_.escapeRegExp(term), 'g')
cur = @editor.getCursorBufferPosition()
matchPoints = []
iterator = (item) =>
matchPointItem =
range: item.range
matchPoints.push(matchPointItem)
@editor.scan(regexp, iterator)
previous = _.filter matchPoints, (point) =>
if @reverse
point.range.start.compare(cur) < 0
else
point.range.start.compare(cur) <= 0
after = _.difference(matchPoints, previous)
after.push(previous...)
after = after.reverse() if @reverse
@matches = after
class Search extends SearchBase
constructor: (@editorView, @vimState) ->
super(@editorView, @vimState)
@viewModel = new SearchViewModel(@)
Search.currentSearch = @
@reverse = @initiallyReversed = false
compose: (input) ->
super(input)
@viewModel.value = @input.characters
class SearchCurrentWord extends SearchBase
@keywordRegex: null
constructor: (@editorView, @vimState) ->
super(@editorView, @vimState)
Search.currentSearch = @
@reverse = @initiallyReversed = false
# 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)
@input = new Input(@getCurrentWordMatch())
getCurrentWord: (onRecursion=false) ->
cursor = @editor.getCursor()
wordRange = cursor.getCurrentWordBufferRange(wordRegex: @keywordRegex)
characters = @editor.getTextInBufferRange(wordRange)
# We are not standing on top of a word, let's try to
# get to the next word and try again
if characters.length is 0 and not onRecursion
if @cursorIsOnEOF()
""
else
cursor.moveToNextWordBoundary(wordRegex: @keywordRegex)
@getCurrentWord(true)
else
characters
cursorIsOnEOF: ->
cursor = @editor.getCursor()
pos = cursor.getMoveNextWordBoundaryBufferPosition(wordRegex: @keywordRegex)
eofPos = @editor.getEofBufferPosition()
pos.row == eofPos.row && pos.column == 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
class BracketMatchingMotion extends SearchBase
@keywordRegex: null
constructor: (@editorView, @vimState) ->
super(@editorView, @vimState)
Search.currentSearch = @
@reverse = @initiallyReversed = false
@characters = [')','(','}','{',']','[']
@charactersMatching = ['(',')','{','}','[',']']
@reverseSearch = [true,false,true,false,true,false]
# FIXME: This must depend on the current language
@input = new Input(@getCurrentWordMatch())
getCurrentWord: (onRecursion=false) ->
cursor = @editor.getCursor()
tempPoint = cursor.getBufferPosition().toArray()
@character = @editor.getTextInBufferRange([cursor.getBufferPosition(),new Point(tempPoint[0],tempPoint[1] + 1)])
@startUp = false;
index = @characters.indexOf(@character)
if index >= 0
@matching = @charactersMatching[index]
@reverse = @reverseSearch[index]
else
@startUp = true
@character
getCurrentWordMatch: ->
characters = @getCurrentWord()
characters
isComplete: -> true
searchFor:(character) ->
term = character
regexp =
new RegExp(_.escapeRegExp(term), 'g')
cur = @editor.getCursorBufferPosition()
matchPoints = []
iterator = (item) =>
matchPointItem =
range: item.range
matchPoints.push(matchPointItem)
@editor.scan(regexp, iterator)
previous = _.filter matchPoints, (point) =>
if @reverse
point.range.start.compare(cur) < 0
else
point.range.start.compare(cur) <= 0
if @reverse
after = []
after.push(previous...)
after = after.reverse()
else
after = _.difference(matchPoints, previous)
matches = after
matches
select: (count=1) ->
@scan()
cur = if @startUp then @startUpPos else @editor.getCursorBufferPosition()
@match count, (pos) =>
if @reverse
tempPoint = cur.toArray()
@editor.setSelectedBufferRange([pos.range.start, new Point(tempPoint[0],tempPoint[1] + 1)], {reversed: true})
else
tempPoint = pos.range.start.toArray()
@editor.setSelectedBufferRange([ cur, new Point(tempPoint[0],tempPoint[1] + 1)], {reversed: true})
[true]
scan: ->
if @startUp
@startUpPos = @editor.getCursorBufferPosition()
min = -1
iwin = -1
for i in [0..@characters.length - 1]
matchesCharacter = @searchFor(@characters[i])
if matchesCharacter.length > 0
dst = matchesCharacter[0].range.start.toArray()
if @startUpPos.toArray()[0] == dst[0] and @startUpPos.toArray()[1] < dst[1]
if dst[1] < min or min == -1
line = dst[0]
min = dst[1]
iwin = i
if iwin != -1
@editor.setCursorBufferPosition(new Point(line,min))
@character = @characters[iwin]
@matching = @charactersMatching[iwin]
@reverse = @reverseSearch[iwin]
matchesCharacter = @searchFor(@character)
matchesMatching = @searchFor(@matching)
if matchesMatching.length == 0
@matches = []
else
charIndex = 0;
matchIndex = 0;
counter = 1;
winner = -1
if @reverse
compVal = 1
else
compVal = -1
while counter > 0
if matchIndex < matchesMatching.length and charIndex < matchesCharacter.length
if matchesCharacter[charIndex].range.compare(matchesMatching[matchIndex].range) == compVal
counter = counter + 1
charIndex = charIndex + 1
else
counter = counter - 1
winner = matchIndex
matchIndex = matchIndex + 1
else if matchIndex < matchesMatching.length
counter = counter - 1
winner = matchIndex
matchIndex = matchIndex + 1
else
break
retVal = []
if counter == 0
retVal.push(matchesMatching[winner])
@matches = retVal
if @matches.length == 0 and @startUp
@editor.setCursorBufferPosition(@startUpPos)
execute: (count=1) ->
super(count) if @input.characters.length > 0
module.exports = {Search, SearchCurrentWord,BracketMatchingMotion}