Address parsing
This commit is contained in:
parent
1ec4589831
commit
a7504aa590
2 changed files with 144 additions and 2 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
ExViewModel = require './ex-view-model'
|
ExViewModel = require './ex-view-model'
|
||||||
Ex = require './ex'
|
Ex = require './ex'
|
||||||
|
Find = require './find'
|
||||||
|
|
||||||
class CommandError
|
class CommandError
|
||||||
constructor: (@message) ->
|
constructor: (@message) ->
|
||||||
|
|
@ -9,9 +10,124 @@ class Command
|
||||||
constructor: (@editor, @exState) ->
|
constructor: (@editor, @exState) ->
|
||||||
@viewModel = new ExViewModel(@)
|
@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) ->
|
execute: (input) ->
|
||||||
return unless input.characters.length > 0
|
@vimState = @exState.globalExState.vim?.getEditorState(@editor)
|
||||||
[command, args...] = input.characters.split(" ")
|
# 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]
|
func = Ex.singleton()[command]
|
||||||
if func?
|
if func?
|
||||||
|
|
|
||||||
26
lib/find.coffee
Normal file
26
lib/find.coffee
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
module.exports = {
|
||||||
|
findLines: (lines, pattern) ->
|
||||||
|
# TODO: There's gotta be a better way to do this. Can we use vim-mode's
|
||||||
|
# search or find-and-replace maybe?
|
||||||
|
return (i for line, i in lines when line.match(pattern))
|
||||||
|
|
||||||
|
findNext: (lines, pattern, curLine) ->
|
||||||
|
lines = @findLines(lines, pattern)
|
||||||
|
if lines.length == 0
|
||||||
|
return null
|
||||||
|
more = (i for i in lines when i > curLine)
|
||||||
|
if more.length > 0
|
||||||
|
return more[0]
|
||||||
|
else
|
||||||
|
return lines[0]
|
||||||
|
|
||||||
|
findPrevious: (lines, pattern, curLine) ->
|
||||||
|
lines = @findLines(lines, pattern)
|
||||||
|
if lines.length == 0
|
||||||
|
return null
|
||||||
|
less = (i for i in lines when i < curLine)
|
||||||
|
if less.length > 0
|
||||||
|
return less[0]
|
||||||
|
else
|
||||||
|
return lines[lines.length - 1]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue