280 lines
7.9 KiB
CoffeeScript
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}
|