]>
Commit | Line | Data |
---|---|---|
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 |