1 ExViewModel = require './ex-view-model'
3 Find = require './find'
4 CommandError = require './command-error'
7 constructor: (@editor, @exState) ->
8 @viewModel = new ExViewModel(@)
10 parseAddr: (str, curPos) ->
14 # Lines are 0-indexed in Atom, but 1-indexed in vim.
15 addr = @editor.getBuffer().lines.length - 1
16 else if str[0] in ["+", "-"]
17 addr = curPos.row + @parseOffset(str)
18 else if not isNaN(str)
19 addr = parseInt(str) - 1
20 else if str[0] is "'" # Parse Mark...
22 throw new CommandError("Couldn't get access to vim-mode.")
23 mark = @vimState.marks[str[1]]
25 throw new CommandError("Mark #{str} not set.")
26 addr = mark.bufferMarker.range.end.row
28 addr = Find.findNextInBuffer(@editor.buffer, curPos, str[1...-1])
30 throw new CommandError("Pattern not found: #{str[1...-1]}")
32 addr = Find.findPreviousInBuffer(@editor.buffer, curPos, str[1...-1])
34 throw new CommandError("Pattern not found: #{str[1...-1]}")
44 o = parseInt(str[1..])
51 @vimState = @exState.globalExState.vim?.getEditorState(@editor)
52 # Command line parsing (mostly) following the rules at
53 # http://pubs.opengroup.org/onlinepubs/9699919799/utilities
54 # /ex.html#tag_20_40_13_03
56 # Steps 1/2: Leading blanks and colons are ignored.
58 cl = cl.replace(/^(:|\s)*/, '')
59 return unless cl.length > 0
61 # Step 3: If the first character is a ", ignore the rest of the line
65 # Step 4: Address parsing
66 lastLine = @editor.getBuffer().lines.length - 1
77 '[\[\]<>'`"^.(){}a-zA-Z]| # Marks
79 \?.*?[^\\]\?| # Backwards search
80 [+-]\d* # Current line +/- a number of lines
81 )((?:\s*[+-]\d*)*) # Line offset
84 ( # Same as first address
88 '[\[\]<>'`"^.(){}a-zA-Z]|
96 [match, addr1, off1, addr2, off2] = cl.match(addrPattern)
98 curPos = @editor.getCursorBufferPosition()
101 address1 = @parseAddr(addr1, curPos)
103 # If no addr1 is given (,+3), assume it is '.'
104 address1 = curPos.row
106 address1 += @parseOffset(off1)
108 address1 = 0 if address1 is -1
110 if address1 < 0 or address1 > lastLine
111 throw new CommandError('Invalid range')
114 address2 = @parseAddr(addr2, curPos)
116 address2 += @parseOffset(off2)
118 if address2 < 0 or address2 > lastLine
119 throw new CommandError('Invalid range')
121 if address2 < address1
122 throw new CommandError('Backwards range given')
124 range = [address1, if address2? then address2 else address1]
125 cl = cl[match?.length..]
127 # Step 5: Leading blanks are ignored
130 # Step 6a: If no command is specified, go to the last specified address
132 @editor.setCursorBufferPosition([range[1], 0])
135 # Ignore steps 6b and 6c since they only make sense for print commands and
136 # print doesn't make sense
138 # Ignore step 7a since flags are only useful for print
140 # Step 7b: :k<valid mark> is equal to :mark <valid mark> - only a-zA-Z is
141 # in vim-mode for now
142 if cl.length is 2 and cl[0] is 'k' and /[a-z]/i.test(cl[1])
145 else if not /[a-z]/i.test(cl[0])
149 [m, command, args] = cl.match(/^(\w+)(.*)/)
151 # If the command matches an existing one exactly, execute that one
152 if (func = Ex.singleton()[command])?
155 # Step 8: Match command against existing commands
156 matching = (name for name, val of Ex.singleton() when \
157 name.indexOf(command) is 0)
161 command = matching[0]
163 func = Ex.singleton()[command]
167 throw new CommandError("Not an editor command: #{input.characters}")
169 module.exports = Command