2 -- Vis Lua plugin API standard library
8 --- Map a new operator.
10 -- Sets up a mapping in normal, visual and operator pending mode.
11 -- The operator function will receive the @{File}, @{Range} and position
12 -- to operate on and is expected to return the new cursor position.
14 -- @tparam string key the key to associate with the new operator
15 -- @tparam function operator the operator logic implemented as Lua function
16 -- @tparam[opt] string help the single line help text as displayed in `:help`
17 -- @treturn bool whether the new operator could be installed
19 -- vis:operator_new("gq", function(file, range, pos)
20 -- local status, out, err = vis:pipe(file, range, "fmt")
25 -- file:insert(range.start, out)
27 -- return range.start -- new cursor location
28 -- end, "Formating operator, filter range through fmt(1)")
30 vis
.operator_new
= function(vis
, key
, operator
, help
)
31 local id
= vis
:operator_register(operator
)
35 local binding
= function()
38 vis
:map(vis
.modes
.NORMAL
, key
, binding
, help
)
39 vis
:map(vis
.modes
.VISUAL
, key
, binding
, help
)
40 vis
:map(vis
.modes
.OPERATOR_PENDING
, key
, binding
, help
)
46 -- Sets up a mapping in normal, visual and operator pending mode.
47 -- The motion function will receive the @{Window} and an initial position
48 -- (in bytes from the start of the file) as argument and is expected to
49 -- return the resulting position.
50 -- @tparam string key the key to associate with the new mption
51 -- @tparam function motion the motion logic implemented as Lua function
52 -- @tparam[opt] string help the single line help text as displayed in `:help`
53 -- @treturn bool whether the new motion could be installed
55 -- vis:motion_new("<C-l>", function(win, pos)
57 -- end, "Advance to next byte")
59 vis
.motion_new
= function(vis
, key
, motion
, help
)
60 local id
= vis
:motion_register(motion
)
64 local binding
= function()
67 vis
:map(vis
.modes
.NORMAL
, key
, binding
, help
)
68 vis
:map(vis
.modes
.VISUAL
, key
, binding
, help
)
69 vis
:map(vis
.modes
.OPERATOR_PENDING
, key
, binding
, help
)
73 --- Map a new text object.
75 -- Sets up a mapping in visual and operator pending mode.
76 -- The text object function will receive the @{Window} and an initial
77 -- position(in bytes from the start of the file) as argument and is
78 -- expected to return the resulting range or `nil`.
79 -- @tparam string key the key associated with the new text object
80 -- @tparam function textobject the text object logic implemented as Lua function
81 -- @tparam[opt] string help the single line help text as displayed in `:help`
82 -- @treturn bool whether the new text object could be installed
84 -- vis:textobject_new("<C-l>", function(win, pos)
86 -- end, "Single byte text object")
88 vis
.textobject_new
= function(vis
, key
, textobject
, help
)
89 local id
= vis
:textobject_register(textobject
)
93 local binding
= function()
96 vis
:map(vis
.modes
.VISUAL
, key
, binding
, help
)
97 vis
:map(vis
.modes
.OPERATOR_PENDING
, key
, binding
, help
)
101 --- Check whether a Lua module exists
103 -- Checks whether a subsequent @{require} call will succeed.
104 -- @tparam string name the module name to check
105 -- @treturn bool whether the module was found
106 vis
.module_exist
= function(vis
, name
)
107 for _
, searcher
in ipairs(package
.searchers
or package
.loaders
) do
108 local loader
= searcher(name
)
109 if type(loader
) == 'function' then
118 if not vis
:module_exist('lpeg') then
119 vis
:info('WARNING: could not find lpeg module')
120 elseif not vis
:module_exist('lexer') then
121 vis
:info('WARNING: could not find lexer module')
123 vis
.lexers
= require('lexer')
124 vis
.lpeg
= require('lpeg')
129 -- User scripts can subscribe Lua functions to certain events. Multiple functions
130 -- can be associated with the same event. They will be called in the order they were
131 -- registered. The first function which returns a non `nil` value terminates event
132 -- propagation. The remaining event handler will not be called.
134 -- Keep in mind that the editor is blocked while the event handlers
135 -- are being executed, avoid long running tasks.
142 FILE_CLOSE
= "Event::FILE_CLOSE", -- see @{file_close}
143 FILE_OPEN
= "Event::FILE_OPEN", -- see @{file_open}
144 FILE_SAVE_POST
= "Event::FILE_SAVE_POST", -- see @{file_save_post}
145 FILE_SAVE_PRE
= "Event::FILE_SAVE_PRE", -- see @{file_save_pre}
146 INIT
= "Event::INIT", -- see @{init}
147 INPUT
= "Event::INPUT", -- see @{input}
148 QUIT
= "Event::QUIT", -- see @{quit}
149 START
= "Event::START", -- see @{start}
150 WIN_CLOSE
= "Event::WIN_CLOSE", -- see @{win_close}
151 WIN_HIGHLIGHT
= "Event::WIN_HIGHLIGHT", -- see @{win_highlight}
152 WIN_OPEN
= "Event::WIN_OPEN", -- see @{win_open}
153 WIN_STATUS
= "Event::WIN_STATUS", -- see @{win_status}
154 TERM_CSI
= "Event::TERM_CSI", -- see @{term_csi}
157 events
.file_close
= function(...) events
.emit(events
.FILE_CLOSE
, ...) end
158 events
.file_open
= function(...) events
.emit(events
.FILE_OPEN
, ...) end
159 events
.file_save_post
= function(...) events
.emit(events
.FILE_SAVE_POST
, ...) end
160 events
.file_save_pre
= function(...) return events
.emit(events
.FILE_SAVE_PRE
, ...) end
161 events
.init
= function(...) events
.emit(events
.INIT
, ...) end
162 events
.input
= function(...) return events
.emit(events
.INPUT
, ...) end
163 events
.quit
= function(...) events
.emit(events
.QUIT
, ...) end
164 events
.start
= function(...) events
.emit(events
.START
, ...) end
165 events
.win_close
= function(...) events
.emit(events
.WIN_CLOSE
, ...) end
166 events
.win_highlight
= function(...) events
.emit(events
.WIN_HIGHLIGHT
, ...) end
167 events
.win_open
= function(...) events
.emit(events
.WIN_OPEN
, ...) end
168 events
.win_status
= function(...) events
.emit(events
.WIN_STATUS
, ...) end
169 events
.term_csi
= function(...) events
.emit(events
.TERM_CSI
, ...) end
173 --- Subscribe to an event.
175 -- Register an event handler.
176 -- @tparam string event the event name
177 -- @tparam function handler the event handler
178 -- @tparam[opt] int index the index at which to insert the handler (1 is the highest priority)
180 -- vis.events.subscribe(vis.events.FILE_SAVE_PRE, function(file, path)
181 -- -- do something useful
184 events
.subscribe
= function(event
, handler
, index
)
185 if not event
then error("Invalid event name") end
186 if type(handler
) ~= 'function' then error("Invalid event handler") end
187 if not handlers
[event
] then handlers
[event
] = {} end
188 events
.unsubscribe(event
, handler
)
189 table.insert(handlers
[event
], index
or #handlers
[event
]+1, handler
)
192 --- Unsubscribe from an event.
194 -- Remove a registered event handler.
195 -- @tparam string event the event name
196 -- @tparam function handler the event handler to unsubscribe
197 -- @treturn bool whether the handler was successfully removed
198 events
.unsubscribe
= function(event
, handler
)
199 local h
= handlers
[event
]
200 if not h
then return end
202 if h
[i
] == handler
then
212 -- Invokes all event handlers in the order they were registered.
213 -- Passes all arguments to the handler. The first handler which returns a non `nil`
214 -- value terminates the event propagation. The other handlers will not be called.
216 -- @tparam string event the event name
217 -- @tparam ... ... the remaining paramters are passed on to the handler
218 events
.emit
= function(event
, ...)
219 local h
= handlers
[event
]
220 if not h
then return end
222 local ret
= h
[i
](...)
223 if type(ret
) ~= 'nil' then return ret
end
232 --- The file type associated with this window.
233 -- @tfield string syntax the syntax lexer name or `nil` if unset
235 --- Change syntax lexer to use for this window
236 -- @function set_syntax
237 -- @tparam string syntax the syntax lexer name or `nil` to disable syntax highlighting
238 -- @treturn bool whether the lexer could be changed
239 vis
.types
.window
.set_syntax
= function(win
, syntax
)
241 local lexers
= vis
.lexers
243 win
:style_define(win
.STYLE_DEFAULT
, lexers
.STYLE_DEFAULT
or '')
244 win
:style_define(win
.STYLE_CURSOR
, lexers
.STYLE_CURSOR
or '')
245 win
:style_define(win
.STYLE_CURSOR_PRIMARY
, lexers
.STYLE_CURSOR_PRIMARY
or '')
246 win
:style_define(win
.STYLE_CURSOR_LINE
, lexers
.STYLE_CURSOR_LINE
or '')
247 win
:style_define(win
.STYLE_SELECTION
, lexers
.STYLE_SELECTION
or '')
248 win
:style_define(win
.STYLE_LINENUMBER
, lexers
.STYLE_LINENUMBER
or '')
249 win
:style_define(win
.STYLE_LINENUMBER_CURSOR
, lexers
.STYLE_LINENUMBER_CURSOR
or '')
250 win
:style_define(win
.STYLE_COLOR_COLUMN
, lexers
.STYLE_COLOR_COLUMN
or '')
251 win
:style_define(win
.STYLE_STATUS
, lexers
.STYLE_STATUS
or '')
252 win
:style_define(win
.STYLE_STATUS_FOCUSED
, lexers
.STYLE_STATUS_FOCUSED
or '')
253 win
:style_define(win
.STYLE_SEPARATOR
, lexers
.STYLE_SEPARATOR
or '')
254 win
:style_define(win
.STYLE_INFO
, lexers
.STYLE_INFO
or '')
255 win
:style_define(win
.STYLE_EOF
, lexers
.STYLE_EOF
or '')
257 if syntax
== nil or syntax
== 'off' then
262 if not lexers
.load
then return false end
263 local lexer
= lexers
.load(syntax
)
264 if not lexer
then return false end
266 for token_name
, id
in pairs(lexer
._TOKENSTYLES
) do
267 local style
= lexers
['STYLE_'..string.upper(token_name
)] or lexer
._EXTRASTYLES
[token_name
]
268 win
:style_define(id
, style
)
278 --- Check whether LPeg pattern matches at a given file position.
279 -- @function match_at
280 -- @param pattern the LPeg pattern
281 -- @tparam int pos the absolute file position which should be tested for a match
282 -- @tparam[opt] int horizon the number of bytes around `pos` to consider (defaults to 1K)
283 -- @treturn int start,end the range of the matched region or `nil`
284 vis
.types
.file
.match_at
= function(file
, pattern
, pos
, horizon
)
285 horizon
= horizon
or 1024
286 local lpeg
= vis
.lpeg
287 if not lpeg
then return nil end
288 local before
, after
= pos
- horizon
, pos
+ horizon
289 if before
< 0 then before
= 0 end
290 local data
= file
:content(before
, after
- before
)
291 local string_pos
= pos
- before
+ 1
294 local p
= lpeg
.P
{ I
* pattern
* I
+ 1 * lpeg
.V(1) }
297 s
, e
= p
:match(data
, s
)
298 if not s
then return nil end
299 if s
<= string_pos
and string_pos
< e
then
300 return before
+ s
- 1, before
+ e
- 1