Address parsing

This commit is contained in:
jazzpi 2015-03-21 19:03:58 +01:00
parent 1ec4589831
commit a7504aa590
2 changed files with 144 additions and 2 deletions

View file

@ -1,5 +1,6 @@
ExViewModel = require './ex-view-model'
Ex = require './ex'
Find = require './find'
class CommandError
constructor: (@message) ->
@ -9,9 +10,124 @@ class Command
constructor: (@editor, @exState) ->
@viewModel = new ExViewModel(@)
parseAddr: (str, curLine) ->
if str == '.'
addr = curLine
else if str == '$'
# Lines are 0-indexed in Atom, but 1-indexed in vim.
addr = @editor.getBuffer().lines.length - 1
else if str[0] in ["+", "-"]
addr = curLine + @parseOffset(str)
else if not isNaN(str)
addr = parseInt(str) - 1
else if str[0] == "'" # Parse Mark...
unless @vimState?
throw new CommandError("Couldn't get access to vim-mode.")
mark = @vimState.marks[str[1]]
unless mark?
throw new CommandError('Mark ' + str + ' not set.')
addr = mark.bufferMarker.range.end.row
else if str[0] == "/" # TODO: Parse forward search
addr = Find.findNext(@editor.buffer.lines, str[1...-1], curLine)
unless addr?
throw new CommandError('Pattern not found: ' + str[1...-1])
else if str[0] == "?" # TODO: Parse backwards search
addr = Find.findPrevious(@editor.buffer.lines, str[1...-1], curLine)
unless addr?
throw new CommandError('Pattern not found: ' + str[1...-1])
return addr
parseOffset: (str) ->
if str.length == 0
return 0
if str.length == 1
o = 1
else
o = parseInt(str[1..])
if str[0] == '+'
return o
else
return -o
execute: (input) ->
return unless input.characters.length > 0
[command, args...] = input.characters.split(" ")
@vimState = @exState.globalExState.vim?.getEditorState(@editor)
# Command line parsing (mostly) following the rules at
# http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ex.html#tag_20_40_13_03
# Steps 1/2: Leading blanks and colons are ignored.
cl = input.characters
cl = cl.replace(/^(:|\s)*/, '')
return unless cl.length > 0
# Step 3: If the first character is a ", ignore the rest of the line
if cl[0] == '"'
return
# Step 4: Address parsing
lastLine = @editor.getBuffer().lines.length - 1
if cl[0] == '%'
range = [0, lastLine]
cl = cl[1..]
else
addrPattern = ///^
(?: # First address
(
\.| # Current line
\$| # Last line
\d+| # n-th line
'[\[\]<>'`"^.(){}a-zA-Z]| # Marks
/.*?[^\\]/| # Regex
\?.*?[^\\]\?| # Backwards search
[+-]\d* # Current line +/- a number of lines
)((?:\s*[+-]\d*)*) # Line offset
)?
(?:, # Second address
( # Same as first address
\.|
\$|
\d+|
'[\[\]<>'`"^.(){}a-zA-Z]|
/.*?[^\\]/|
\?.*?[^\\]\?|
[+-]\d*
)((?:\s*[+-]\d*)*)
)?
///
[match, addr1, off1, addr2, off2] = cl.match(addrPattern)
curLine = @editor.getCursorBufferPosition().row
if addr1?
address1 = @parseAddr(addr1, curLine)
else
# If no addr1 is given (,+3), assume it is '.'
address1 = curLine
if off1?
address1 += @parseOffset(off1)
if address1 < 0 or address1 > lastLine
throw new CommandError('Invalid range')
if addr2?
address2 = @parseAddr(addr2, curLine)
if off2?
address2 += @parseOffset(off2)
if address2 < 0 or address2 > lastLine
throw new CommandError('Invalid range')
if address2 < address1
throw new CommandError('Backwards range given')
range = [address1, if address2? then address2 else address1]
cl = cl[match?.length..]
# Step 5: Leading blanks are ignored
cl = cl.trimLeft()
# Vim behavior: If no command is specified, go to the specified length
if cl.length == 0
@editor.setCursorBufferPosition([range[1], 0])
return
func = Ex.singleton()[command]
if func?