]> git.r.bdr.sh - rbdr/dotfiles/blob - atom/packages/ex-mode/lib/ex.coffee
c159534c4f62af3f1d977327a2af4c0f368c6f0d
[rbdr/dotfiles] / atom / packages / ex-mode / lib / ex.coffee
1 path = require 'path'
2 CommandError = require './command-error'
3
4 trySave = (func) ->
5 deferred = Promise.defer()
6
7 try
8 func()
9 deferred.resolve()
10 catch error
11 if error.message.endsWith('is a directory')
12 atom.notifications.addWarning("Unable to save file: #{error.message}")
13 else if error.path?
14 if error.code is 'EACCES'
15 atom.notifications
16 .addWarning("Unable to save file: Permission denied '#{error.path}'")
17 else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST']
18 atom.notifications.addWarning("Unable to save file '#{error.path}'",
19 detail: error.message)
20 else if error.code is 'EROFS'
21 atom.notifications.addWarning(
22 "Unable to save file: Read-only file system '#{error.path}'")
23 else if (errorMatch =
24 /ENOTDIR, not a directory '([^']+)'/.exec(error.message))
25 fileName = errorMatch[1]
26 atom.notifications.addWarning("Unable to save file: A directory in the "+
27 "path '#{fileName}' could not be written to")
28 else
29 throw error
30
31 deferred.promise
32
33 getFullPath = (filePath) ->
34 return filePath if path.isAbsolute(filePath)
35 return path.join(atom.project.getPath(), filePath)
36
37 replaceGroups = (groups, replString) ->
38 arr = replString.split('')
39 offset = 0
40 cdiff = 0
41
42 while (m = replString.match(/(?:[^\\]|^)\\(\d)/))?
43 group = groups[m[1]] or ''
44 i = replString.indexOf(m[0])
45 l = m[0].length
46 replString = replString.slice(i + l)
47 arr[i + offset...i + offset + l] = (if l is 2 then '' else m[0][0]) +
48 group
49 arr = arr.join('').split ''
50 offset += i + l - group.length
51
52 return arr.join('').replace(/\\\\(\d)/, '\\$1')
53
54 class Ex
55 @singleton: =>
56 @ex ||= new Ex
57
58 @registerCommand: (name, func) =>
59 @singleton()[name] = func
60
61 quit: ->
62 atom.workspace.getActivePane().destroyActiveItem()
63
64 q: => @quit()
65
66 tabedit: (range, args) ->
67 args = args.trim()
68 filePaths = args.split(' ')
69 pane = atom.workspace.getActivePane()
70 if filePaths? and filePaths.length > 0
71 for file in filePaths
72 do -> atom.workspace.openURIInPane file, pane
73 else
74 atom.workspace.openURIInPane('', pane)
75
76 tabe: (args...) => @tabedit(args...)
77
78 tabnew: (args...) => @tabedit(args...)
79
80 tabclose: => @quit()
81
82 tabc: => @tabclose()
83
84 tabnext: ->
85 pane = atom.workspace.getActivePane()
86 pane.activateNextItem()
87
88 tabn: => @tabnext()
89
90 tabprevious: ->
91 pane = atom.workspace.getActivePane()
92 pane.activatePreviousItem()
93
94 tabp: => @tabprevious()
95
96 edit: (range, filePath) ->
97 filePath = filePath.trim()
98 if filePath.indexOf(' ') isnt -1
99 throw new CommandError('Only one file name allowed')
100 buffer = atom.workspace.getActiveTextEditor().buffer
101 filePath = buffer.getPath() if filePath is ''
102 buffer.setPath(getFullPath(filePath))
103 buffer.load()
104
105 e: (args...) => @edit(args...)
106
107 enew: ->
108 buffer = atom.workspace.getActiveTextEditor().buffer
109 buffer.setPath(undefined)
110 buffer.load()
111
112 write: (range, filePath) ->
113 filePath = filePath.trim()
114 deferred = Promise.defer()
115
116 pane = atom.workspace.getActivePane()
117 editor = atom.workspace.getActiveTextEditor()
118 if atom.workspace.getActiveTextEditor().getPath() isnt undefined
119 if filePath.length > 0
120 editorPath = editor.getPath()
121 fullPath = getFullPath(filePath)
122 trySave(-> editor.saveAs(fullPath))
123 .then ->
124 deferred.resolve()
125 editor.buffer.setPath(editorPath)
126 else
127 trySave(-> editor.save())
128 .then deferred.resolve
129 else
130 if filePath.length > 0
131 fullPath = getFullPath(filePath)
132 trySave(-> editor.saveAs(fullPath))
133 .then deferred.resolve
134 else
135 fullPath = atom.showSaveDialogSync()
136 if fullPath?
137 trySave(-> editor.saveAs(fullPath))
138 .then deferred.resolve
139
140 deferred.promise
141
142 w: (args...) =>
143 @write(args...)
144
145 wq: (args...) =>
146 @write(args...).then => @quit()
147
148 x: (args...) => @wq(args...)
149
150 wa: ->
151 atom.workspace.saveAll()
152
153 split: (range, args) ->
154 args = args.trim()
155 filePaths = args.split(' ')
156 filePaths = undefined if filePaths.length is 1 and filePaths[0] is ''
157 pane = atom.workspace.getActivePane()
158 if filePaths? and filePaths.length > 0
159 newPane = pane.splitUp()
160 for file in filePaths
161 do ->
162 atom.workspace.openURIInPane file, newPane
163 else
164 pane.splitUp(copyActiveItem: true)
165
166 sp: (args...) => @split(args...)
167
168 substitute: (range, args) ->
169 args = args.trimLeft()
170 delim = args[0]
171 if /[a-z]/i.test(delim)
172 throw new CommandError(
173 "Regular expressions can't be delimited by letters")
174 delimRE = new RegExp("[^\\\\]#{delim}")
175 spl = []
176 args_ = args[1..]
177 while (i = args_.search(delimRE)) isnt -1
178 spl.push args_[..i]
179 args_ = args_[i + 2..]
180 if args_.length is 0 and spl.length is 3
181 throw new CommandError('Trailing characters')
182 else if args_.length isnt 0
183 spl.push args_
184 if spl.length > 3
185 throw new CommandError('Trailing characters')
186 spl[1] ?= ''
187 spl[2] ?= ''
188
189 try
190 pattern = new RegExp(spl[0], spl[2])
191 catch e
192 if e.message.indexOf('Invalid flags supplied to RegExp constructor') is 0
193 # vim only says 'Trailing characters', but let's be more descriptive
194 throw new CommandError("Invalid flags: #{e.message[45..]}")
195 else if e.message.indexOf('Invalid regular expression: ') is 0
196 throw new CommandError("Invalid RegEx: #{e.message[27..]}")
197 else
198 throw e
199
200 buffer = atom.workspace.getActiveTextEditor().buffer
201 cp = buffer.history.createCheckpoint()
202 for line in [range[0]..range[1]]
203 buffer.scanInRange(pattern,
204 [[line, 0], [line, buffer.lines[line].length]],
205 ({match, matchText, range, stop, replace}) ->
206 replace(replaceGroups(match[..], spl[1]))
207 )
208 buffer.history.groupChangesSinceCheckpoint(cp)
209
210 s: (args...) => @substitute(args...)
211
212 vsplit: (range, args) ->
213 args = args.trim()
214 filePaths = args.split(' ')
215 filePaths = undefined if filePaths.length is 1 and filePaths[0] is ''
216 pane = atom.workspace.getActivePane()
217 if filePaths? and filePaths.length > 0
218 newPane = pane.splitLeft()
219 for file in filePaths
220 do ->
221 atom.workspace.openURIInPane file, newPane
222 else
223 pane.splitLeft(copyActiveItem: true)
224
225 vsp: (args...) => @vsplit(args...)
226
227 delete: (range) ->
228 range = [[range[0], 0], [range[1] + 1, 0]]
229 atom.workspace.getActiveTextEditor().buffer.setTextInRange(range, '')
230
231 module.exports = Ex