5
5
import Base. shell_split
6
6
using Base: find_source_file
7
7
8
+ struct EditorEntry{P,Fn}
9
+ pattern:: P
10
+ fn:: Fn
11
+ wait:: Bool
12
+ takesline:: Bool
13
+ end
14
+ const editors = Vector {EditorEntry} (undef, 0 )
15
+
16
+ struct EditorCommand{C,E}
17
+ parsed_command:: C
18
+ entry:: E
19
+ end
20
+ function parse_command (entry:: EditorEntry , command, path, line)
21
+ if entry. takesline
22
+ cmd = entry. fn (command, path, line)
23
+ if ! isnothing (cmd)
24
+ return EditorCommand (cmd, entry), true
25
+ end
26
+ end
27
+
28
+ cmd = entry. fn (command, path)
29
+ if ! isnothing (cmd)
30
+ return EditorCommand (cmd, entry), false
31
+ end
32
+
33
+ nothing , false
34
+ end
35
+
36
+ function Base. run (x:: EditorCommand{Cmd} )
37
+ if ! x. entry. wait
38
+ run (pipeline (x. parsed_command, stderr = stderr ), wait= false )
39
+ else
40
+ run (x. parsed_command)
41
+ end
42
+ end
43
+ Base. run (x:: EditorCommand{Function} ) = x. parsed_command ()
44
+
45
+ """
46
+ define_editor(fn, pattern;wait=false)
47
+
48
+ Define a new editor matching `pattern` that can be used to open a file
49
+ (possibly at a given line number) using `fn`.
50
+
51
+ The `fn` argument is a function that determines how to open a file with the
52
+ given editor. It should take 2 or 3 arguments, as follows:
53
+
54
+ * `command` - an array of strings representing the editor command. It can be
55
+ safely interpolated into a command created using backtick notation.
56
+ * `path` - the path to the source file to open
57
+ * `line` - the optional line number to open to; if specified the returned
58
+ command must open the file at the given line.
59
+
60
+ `fn` must return either an appropriate `Cmd` object to open a file, a
61
+ zero-argument function that will open the file directly, or `nothing`.
62
+ Returning a `Cmd` is preferred over returning a function. Use `nothing` to
63
+ indicate that this editor is not appropriate for the current environment and
64
+ another editor should be attempted.
65
+
66
+ The `pattern` argument is a string, regular expression, or an array of strings
67
+ and regular expressions. For the `fn` to be called one of the patterns must
68
+ match the value of `EDITOR`, `VISUAL` or `JULIA_EDITOR`. For strings, only
69
+ whole words can match (i.e. "vi" doesn't match "vim -g" but will match
70
+ "/usr/bin/vi -m").
71
+
72
+ If multiple defined editors match, the one most recently defined will be
73
+ used.
74
+
75
+ By default julia does not wait for the editor to close, running it in the
76
+ background. However, if the editor is terminal based, you will probably want to
77
+ set `wait=true` and julia will wait for the editor to close before resuming.
78
+
79
+ If no editor entry can be found, then a file is opened by running
80
+ `\$ command \$ path`.
81
+
82
+ Note that many editors are already defined. All of the following commands
83
+ should already work:
84
+
85
+ - emacs
86
+ - vim
87
+ - nvim
88
+ - nano
89
+ - textmate
90
+ - mate
91
+ - kate
92
+ - subl
93
+ - atom
94
+ - notepad++
95
+ - Visual Studio Code
96
+ - open
97
+
98
+ # Example:
99
+ The following defines the usage of terminal-based `emacs`:
100
+
101
+ define_editor(r"\b emacs\b .*(-nw|--no-window-system)",
102
+ wait=true) do cmd, path, line
103
+ `\$ cmd +\$ line \$ path`
104
+ end
105
+ """
106
+ function define_editor (fn, pattern; wait= false , priority= 0 )
107
+ nargs = map (x -> x. nargs - 1 , methods (fn). ms)
108
+ has3args = 3 ∈ nargs
109
+ has2args = 2 ∈ nargs
110
+ entry = EditorEntry (pattern, fn, wait, has3args)
111
+ pushfirst! (editors, entry)
112
+
113
+ if ! (has3args || has2args)
114
+ error (" Editor function must take 2 or 3 arguments" )
115
+ end
116
+ end
117
+
118
+ function define_default_editors ()
119
+ define_editor ([" vim" ," vi" ," nvim" ," mvim" ," nano" ],
120
+ wait= true ) do cmd, path, line
121
+ ` $cmd +$line $path `
122
+ end
123
+ define_editor ([r" \b emacs" ," gedit" ,r" \b gvim" ]) do cmd, path, line
124
+ ` $cmd +$line $path `
125
+ end
126
+ define_editor (r" \b emacs\b .*(-nw|--no-window-system)" ,
127
+ wait= true ) do cmd, path, line
128
+ ` $cmd +$line $path `
129
+ end
130
+ define_editor (r" \b emacsclient\b .*(-nw|-t|-tty)" ,
131
+ wait= true ) do cmd, path, line
132
+ ` $cmd +$line $path `
133
+ end
134
+ define_editor ([" textmate" ," mate" ," kate" ]) do cmd, path, line
135
+ ` $cmd $path -l $line `
136
+ end
137
+ define_editor ([r" \b subl" ,r" \b atom" ]) do cmd, path, line
138
+ ` $cmd $path :$line `
139
+ end
140
+ define_editor (" code" ) do cmd, path, line
141
+ ` $cmd -g $path :$line `
142
+ end
143
+ define_editor (r" \b notepad++" ) do cmd, path, line
144
+ ` $cmd $path -n$line `
145
+ end
146
+ if Sys. iswindows ()
147
+ define_editor (r" \b CODE\. EXE\b " i ) do cmd, path, line
148
+ ` $cmd -g $path :$line `
149
+ end
150
+ define_editor (" open" ) do cmd, path, line
151
+ function ()
152
+ # don't emit this ccall on other platforms
153
+ @static if Sys. iswindows ()
154
+ result = ccall ((:ShellExecuteW , " shell32" ), stdcall,
155
+ Int, (Ptr{Cvoid}, Cwstring, Cwstring,
156
+ Ptr{Cvoid}, Ptr{Cvoid}, Cint),
157
+ C_NULL , " open" , path, C_NULL , C_NULL , 10 )
158
+ systemerror (:edit , result ≤ 32 )
159
+ end
160
+ end
161
+ end
162
+ elseif Sys. isapple ()
163
+ define_editor (" open" ) do cmd, path
164
+ ` open -t $path `
165
+ end
166
+ end
167
+ end
168
+
8
169
"""
9
170
editor()
10
171
@@ -21,65 +182,54 @@ function editor()
21
182
default_editor = " emacs"
22
183
end
23
184
# Note: the editor path can include spaces (if escaped) and flags.
24
- args = shell_split (get (ENV ," JULIA_EDITOR" , get (ENV ," VISUAL" , get (ENV ," EDITOR" , default_editor))))
185
+ args = shell_split (get (ENV , " JULIA_EDITOR" ,
186
+ get (ENV ," VISUAL" , get (ENV ," EDITOR" , default_editor))))
25
187
isempty (args) && error (" editor is empty" )
26
188
return args
27
189
end
28
190
191
+ editormatches (pattern:: String , command) =
192
+ occursin (Regex (" \\ b" * pattern* " \\ b" ), command)
193
+ editormatches (pattern:: Regex , command) =
194
+ occursin (pattern, command)
195
+ editormatches (pattern:: AbstractArray , command) =
196
+ any (x -> editormatches (x, command), pattern)
197
+ findeditors (command) =
198
+ filter (e -> editormatches (e. pattern,join (command," " )), editors)
199
+
29
200
"""
30
201
edit(path::AbstractString, line::Integer=0)
31
202
32
203
Edit a file or directory optionally providing a line number to edit the file at.
33
204
Return to the `julia` prompt when you quit the editor. The editor can be changed
34
205
by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable.
206
+ To ensure that the file can be opened at the given line, you may need to
207
+ call `define_editor` first.
35
208
"""
36
209
function edit (path:: AbstractString , line:: Integer = 0 )
210
+ ! isempty (editors) || define_default_editors ()
37
211
command = editor ()
38
- name = basename (first (command))
39
212
if endswith (path, " .jl" )
40
213
f = find_source_file (path)
41
214
f != = nothing && (path = f)
42
215
end
43
- background = true
44
- line_unsupported = false
45
- if startswith (name, " vim." ) || name == " vi" || name == " vim" || name == " nvim" ||
46
- name == " mvim" || name == " nano" ||
47
- name == " emacs" && any (c -> c in [" -nw" , " --no-window-system" ], command) ||
48
- name == " emacsclient" && any (c -> c in [" -nw" , " -t" , " -tty" ], command)
49
- cmd = line != 0 ? ` $command +$line $path ` : ` $command $path `
50
- background = false
51
- elseif startswith (name, " emacs" ) || name == " gedit" || startswith (name, " gvim" )
52
- cmd = line != 0 ? ` $command +$line $path ` : ` $command $path `
53
- elseif name == " textmate" || name == " mate" || name == " kate"
54
- cmd = line != 0 ? ` $command $path -l $line ` : ` $command $path `
55
- elseif name == " rmate"
56
- cmd = line != 0 ? ` $command $path -l $line -f` : ` $command $path -f`
57
- elseif startswith (name, " subl" ) || startswith (name, " atom" )
58
- cmd = line != 0 ? ` $command $path :$line ` : ` $command $path `
59
- elseif name == " code" || (Sys. iswindows () && (uppercase (name) == " CODE.EXE" || uppercase (name) == " CODE.CMD" ))
60
- cmd = line != 0 ? ` $command -g $path :$line ` : ` $command -g $path `
61
- elseif startswith (name, " notepad++" )
62
- cmd = line != 0 ? ` $command $path -n$line ` : ` $command $path `
63
- elseif Sys. isapple () && name == " open"
64
- cmd = ` open -t $path `
65
- line_unsupported = true
66
- else
67
- cmd = ` $command $path `
68
- background = false
69
- line_unsupported = true
70
- end
71
-
72
- if Sys. iswindows () && name == " open"
73
- @static Sys. iswindows () && # don't emit this ccall on other platforms
74
- systemerror (:edit , ccall ((:ShellExecuteW , " shell32" ), stdcall, Int,
75
- (Ptr{Cvoid}, Cwstring, Cwstring, Ptr{Cvoid}, Ptr{Cvoid}, Cint),
76
- C_NULL , " open" , path, C_NULL , C_NULL , 10 ) ≤ 32 )
77
- elseif background
78
- run (pipeline (cmd, stderr = stderr ), wait= false )
79
- else
80
- run (cmd)
216
+
217
+ parsed = nothing
218
+ line_supported = false
219
+ for entry in findeditors (command)
220
+ parsed, line_supported = parse_command (entry, command, path, line)
221
+ isnothing (parsed) || break
222
+ end
223
+ if isnothing (parsed)
224
+ parsed = ` $command $path `
225
+ line_supported = false
226
+ end
227
+
228
+ if line != 0 && ! line_supported
229
+ @info (" Unknown editor: no line number information passed.\n " *
230
+ " The method is defined at line $line ." )
81
231
end
82
- line != 0 && line_unsupported && println ( " Unknown editor: no line number information passed. \n The method is defined at line $line . " )
232
+ run (parsed )
83
233
84
234
nothing
85
235
end
@@ -95,8 +245,8 @@ method to edit. For modules, open the main source file. The module needs to be l
95
245
!!! compat "Julia 1.1"
96
246
`edit` on modules requires at least Julia 1.1.
97
247
98
- The editor can be changed by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment
99
- variable .
248
+ To ensure that the file can be opened at the given line, you may need to call
249
+ `define_editor` first .
100
250
"""
101
251
edit (f) = edit (functionloc (f)... )
102
252
edit (f, @nospecialize t) = edit (functionloc (f,t)... )
0 commit comments