1-- Copyright 2007-2021 Mitchell. See LICENSE. 2 3local M = {} 4 5--[[ This comment is for LuaDoc. 6--- 7-- Textadept's core event structure and handlers. 8-- 9-- Textadept emits events when you do things like create a new buffer, press a 10-- key, click on a menu, etc. You can even emit events yourself using Lua. Each 11-- event has a set of event handlers, which are simply Lua functions called in 12-- the order they were connected to an event. For example, if you created a 13-- module that needs to do something each time Textadept creates a new buffer, 14-- connect a Lua function to the [`events.BUFFER_NEW`]() event: 15-- 16-- events.connect(events.BUFFER_NEW, function() 17-- -- Do something here. 18-- end) 19-- 20-- Events themselves are nothing special. You do not have to declare one before 21-- using it. Events are simply strings containing arbitrary event names. When 22-- either you or Textadept emits an event, Textadept runs all event handlers 23-- connected to the event, passing any given arguments to the event's handler 24-- functions. If an event handler explicitly returns a value that is not `nil`, 25-- Textadept will not call subsequent handlers. This is useful if you want to 26-- stop the propagation of an event like a keypress if your event handler 27-- handled it, or if you want to use the event framework to pass values. 28-- 29-- @field APPLEEVENT_ODOC (string) 30-- Emitted when macOS tells Textadept to open a file. 31-- Arguments: 32-- 33-- * _`uri`_: The UTF-8-encoded URI to open. 34-- @field AUTO_C_CHAR_DELETED (string) 35-- Emitted after deleting a character while an autocompletion or user list is 36-- active. 37-- @field AUTO_C_CANCELED (string) 38-- Emitted when canceling an autocompletion or user list. 39-- @field AUTO_C_COMPLETED (string) 40-- Emitted after inserting an item from an autocompletion list into the 41-- buffer. 42-- Arguments: 43-- 44-- * _`text`_: The selection's text. 45-- * _`position`_: The autocompleted word's beginning position. 46-- @field AUTO_C_SELECTION (string) 47-- Emitted after selecting an item from an autocompletion list, but before 48-- inserting that item into the buffer. 49-- Automatic insertion can be canceled by calling 50-- [`buffer:auto_c_cancel()`]() before returning from the event handler. 51-- Arguments: 52-- 53-- * _`text`_: The selection's text. 54-- * _`position`_: The autocompleted word's beginning position. 55-- @field AUTO_C_SELECTION_CHANGE (string) 56-- Emitted as items are highlighted in an autocompletion or user list. 57-- Arguments: 58-- 59-- * _`id`_: Either the *id* from [`buffer.user_list_show()`]() or `0` for an 60-- autocompletion list. 61-- * _`text`_: The current selection's text. 62-- * _`position`_: The position the list was displayed at. 63-- @field BUFFER_AFTER_SWITCH (string) 64-- Emitted right after switching to another buffer. 65-- The buffer being switched to is `buffer`. 66-- Emitted by [`view.goto_buffer()`](). 67-- @field BUFFER_BEFORE_SWITCH (string) 68-- Emitted right before switching to another buffer. 69-- The buffer being switched from is `buffer`. 70-- Emitted by [`view.goto_buffer()`](). 71-- @field BUFFER_DELETED (string) 72-- Emitted after deleting a buffer. 73-- Emitted by [`buffer.delete()`](). 74-- @field BUFFER_NEW (string) 75-- Emitted after creating a new buffer. 76-- The new buffer is `buffer`. 77-- Emitted on startup and by [`buffer.new()`](). 78-- @field CALL_TIP_CLICK (string) 79-- Emitted when clicking on a calltip. 80-- Arguments: 81-- 82-- * _`position`_: `1` if the up arrow was clicked, 2 if the down arrow was 83-- clicked, and 0 otherwise. 84-- @field CHAR_ADDED (string) 85-- Emitted after the user types a text character into the buffer. 86-- Arguments: 87-- 88-- * _`code`_: The text character's character code. 89-- @field COMMAND_TEXT_CHANGED (string) 90-- Emitted when the text in the command entry changes. 91-- `ui.command_entry:get_text()` returns the current text. 92-- @field DOUBLE_CLICK (string) 93-- Emitted after double-clicking the mouse button. 94-- Arguments: 95-- 96-- * _`position`_: The position double-clicked. 97-- * _`line`_: The line number of the position double-clicked. 98-- * _`modifiers`_: A bit-mask of any modifier keys held down: 99-- `view.MOD_CTRL`, `view.MOD_SHIFT`, `view.MOD_ALT`, and `view.MOD_META`. 100-- On macOS, the Command modifier key is reported as `view.MOD_CTRL` and 101-- Ctrl is `view.MOD_META`. 102-- Note: If you set `view.rectangular_selection_modifier` to 103-- `view.MOD_CTRL`, the "Control" modifier is reported as *both* "Control" 104-- and "Alt" due to a Scintilla limitation with GTK. 105-- @field CSI (string) 106-- Emitted when the terminal version receives an unrecognized CSI sequence. 107-- Arguments: 108-- 109-- * _`cmd`_: The 24-bit CSI command value. The lowest byte contains the 110-- command byte. The second lowest byte contains the leading byte, if any 111-- (e.g. '?'). The third lowest byte contains the intermediate byte, if any 112-- (e.g. '$'). 113-- * _`args`_: Table of numeric arguments of the CSI sequence. 114-- @field DWELL_END (string) 115-- Emitted after `DWELL_START` when the user moves the mouse, presses a key, 116-- or scrolls the view. 117-- Arguments: 118-- 119-- * _`position`_: The position closest to *x* and *y*. 120-- * _`x`_: The x-coordinate of the mouse in the view. 121-- * _`y`_: The y-coordinate of the mouse in the view. 122-- @field DWELL_START (string) 123-- Emitted when the mouse is stationary for [`view.mouse_dwell_time`]() 124-- milliseconds. 125-- Arguments: 126-- 127-- * _`position`_: The position closest to *x* and *y*. 128-- * _`x`_: The x-coordinate of the mouse in the view. 129-- * _`y`_: The y-coordinate of the mouse in the view. 130-- @field ERROR (string) 131-- Emitted when an error occurs. 132-- Arguments: 133-- 134-- * _`text`_: The error message text. 135-- @field FIND (string) 136-- Emitted to find text via the Find & Replace Pane. 137-- Arguments: 138-- 139-- * _`text`_: The text to search for. 140-- * _`next`_: Whether or not to search forward. 141-- @field FIND_TEXT_CHANGED (string) 142-- Emitted when the text in the "Find" field of the Find & Replace Pane 143-- changes. 144-- `ui.find.find_entry_text` contains the current text. 145-- @field FOCUS (string) 146-- Emitted when Textadept receives focus. 147-- This event is never emitted when Textadept is running in the terminal. 148-- @field INDICATOR_CLICK (string) 149-- Emitted when clicking the mouse on text that has an indicator present. 150-- Arguments: 151-- 152-- * _`position`_: The clicked text's position. 153-- * _`modifiers`_: A bit-mask of any modifier keys held down: 154-- `view.MOD_CTRL`, `view.MOD_SHIFT`, `view.MOD_ALT`, and `view.MOD_META`. 155-- On macOS, the Command modifier key is reported as `view.MOD_CTRL` and 156-- Ctrl is `view.MOD_META`. 157-- Note: If you set `view.rectangular_selection_modifier` to 158-- `view.MOD_CTRL`, the "Control" modifier is reported as *both* "Control" 159-- and "Alt" due to a Scintilla limitation with GTK. 160-- @field INDICATOR_RELEASE (string) 161-- Emitted when releasing the mouse after clicking on text that has an 162-- indicator present. 163-- Arguments: 164-- 165-- * _`position`_: The clicked text's position. 166-- @field INITIALIZED (string) 167-- Emitted after Textadept finishes initializing. 168-- @field KEYPRESS (string) 169-- Emitted when pressing a key. 170-- If any handler returns `true`, the key is not inserted into the buffer. 171-- Arguments: 172-- 173-- * _`code`_: The numeric key code. 174-- * _`shift`_: The "Shift" modifier key is held down. 175-- * _`ctrl`_: The "Control" modifier key is held down. 176-- * _`alt`_: The "Alt"/"Option" modifier key is held down. 177-- * _`cmd`_: The "Command" modifier key on macOS is held down. 178-- * _`caps_lock`_: The "Caps Lock" modifier is on. 179-- @field MARGIN_CLICK (string) 180-- Emitted when clicking the mouse inside a sensitive margin. 181-- Arguments: 182-- 183-- * _`margin`_: The margin number clicked. 184-- * _`position`_: The beginning position of the clicked margin's line. 185-- * _`modifiers`_: A bit-mask of any modifier keys held down: 186-- `view.MOD_CTRL`, `view.MOD_SHIFT`, `view.MOD_ALT`, and `view.MOD_META`. 187-- On macOS, the Command modifier key is reported as `view.MOD_CTRL` and 188-- Ctrl is `view.MOD_META`. 189-- Note: If you set `view.rectangular_selection_modifier` to 190-- `view.MOD_CTRL`, the "Control" modifier is reported as *both* "Control" 191-- and "Alt" due to a Scintilla limitation with GTK. 192-- @field MENU_CLICKED (string) 193-- Emitted after selecting a menu item. 194-- Arguments: 195-- 196-- * _`menu_id`_: The numeric ID of the menu item, which was defined in 197-- [`ui.menu()`](). 198-- @field MOUSE (string) 199-- Emitted by the terminal version for an unhandled mouse event. 200-- A handler should return `true` if it handled the event. Otherwise Textadept 201-- will try again. (This side effect for a `false` or `nil` return is useful 202-- for sending the original mouse event to a different view that a handler 203-- has switched to.) 204-- Arguments: 205-- 206-- * _`event`_: The mouse event: `view.MOUSE_PRESS`, `view.MOUSE_DRAG`, or 207-- `view.MOUSE_RELEASE`. 208-- * _`button`_: The mouse button number. 209-- * _`y`_: The y-coordinate of the mouse event, starting from 1. 210-- * _`x`_: The x-coordinate of the mouse event, starting from 1. 211-- * _`shift`_: The "Shift" modifier key is held down. 212-- * _`ctrl`_: The "Control" modifier key is held down. 213-- * _`alt`_: The "Alt"/"Option" modifier key is held down. 214-- @field QUIT (string) 215-- Emitted when quitting Textadept. 216-- When connecting to this event, connect with an index of 1 if the handler 217-- needs to run before Textadept closes all open buffers. If a handler returns 218-- `true`, Textadept does not quit. It is not recommended to return `false` 219-- from a quit handler, as that may interfere with Textadept's normal shutdown 220-- procedure. 221-- Emitted by [`quit()`](). 222-- @field REPLACE (string) 223-- Emitted to replace selected (found) text. 224-- Arguments: 225-- 226-- * _`text`_: The replacement text. 227-- @field REPLACE_ALL (string) 228-- Emitted to replace all occurrences of found text. 229-- Arguments: 230-- 231-- * _`find_text`_: The text to search for. 232-- * _`repl_text`_: The replacement text. 233-- @field RESET_AFTER (string) 234-- Emitted after resetting Textadept's Lua state. 235-- Emitted by [`reset()`](). 236-- Arguments: 237-- 238-- * _`persist`_: Table of data persisted by `events.RESET_BEFORE`. All 239-- handlers will have access to this same table. 240-- @field RESET_BEFORE (string) 241-- Emitted before resetting Textadept's Lua state. 242-- Emitted by [`reset()`](). 243-- Arguments: 244-- 245-- * _`persist`_: Table to store persistent data in for use by 246-- `events.RESET_AFTER`. All handlers will have access to this same table. 247-- @field RESUME (string) 248-- Emitted when resuming Textadept from a suspended state. 249-- This event is only emitted by the terminal version. 250-- @field SAVE_POINT_LEFT (string) 251-- Emitted after leaving a save point. 252-- @field SAVE_POINT_REACHED (string) 253-- Emitted after reaching a save point. 254-- @field SUSPEND (string) 255-- Emitted when suspending Textadept. If any handler returns `true`, Textadept 256-- does not suspend. 257-- This event is only emitted by the terminal version. 258-- @field TAB_CLICKED (string) 259-- Emitted when the user clicks on a buffer tab. 260-- When connecting to this event, connect with an index of 1 if the handler 261-- needs to run before Textadept switches between buffers. 262-- Note that Textadept always displays a context menu on right-click. 263-- Arguments: 264-- 265-- * _`index`_: The numeric index of the clicked tab. 266-- * _`button`_: The mouse button number that was clicked, either `1` (left 267-- button), `2` (middle button), `3` (right button), `4` (wheel up), or `5` 268-- (wheel down). 269-- * _`shift`_: The "Shift" modifier key is held down. 270-- * _`ctrl`_: The "Control" modifier key is held down. 271-- * _`alt`_: The "Alt"/"Option" modifier key is held down. 272-- * _`cmd`_: The "Command" modifier key on macOS is held down. 273-- @field UNFOCUS (string) 274-- Emitted when Textadept loses focus. 275-- This event is never emitted when Textadept is running in the terminal. 276-- @field UPDATE_UI (string) 277-- Emitted after the view is visually updated. 278-- Arguments: 279-- 280-- * _`updated`_: A bitmask of changes since the last update. 281-- 282-- + `buffer.UPDATE_CONTENT` 283-- Buffer contents, styling, or markers have changed. 284-- + `buffer.UPDATE_SELECTION` 285-- Buffer selection has changed (including caret movement). 286-- + `view.UPDATE_V_SCROLL` 287-- Buffer has scrolled vertically. 288-- + `view.UPDATE_H_SCROLL` 289-- Buffer has scrolled horizontally. 290-- @field URI_DROPPED (string) 291-- Emitted after dragging and dropping a URI into a view. 292-- Arguments: 293-- 294-- * _`text`_: The UTF-8-encoded URI dropped. 295-- @field USER_LIST_SELECTION (string) 296-- Emitted after selecting an item in a user list. 297-- Arguments: 298-- 299-- * _`id`_: The *id* from [`buffer.user_list_show()`](). 300-- * _`text`_: The selection's text. 301-- * _`position`_: The position the list was displayed at. 302-- @field VIEW_NEW (string) 303-- Emitted after creating a new view. 304-- The new view is `view`. 305-- Emitted on startup and by [`view.split()`](). 306-- @field VIEW_BEFORE_SWITCH (string) 307-- Emitted right before switching to another view. 308-- The view being switched from is `view`. 309-- Emitted by [`ui.goto_view()`](). 310-- @field VIEW_AFTER_SWITCH (string) 311-- Emitted right after switching to another view. 312-- The view being switched to is `view`. 313-- Emitted by [`ui.goto_view()`](). 314-- @field ZOOM (string) 315-- Emitted after changing [`view.zoom`](). 316-- Emitted by [`view.zoom_in()`]() and [`view.zoom_out()`](). 317module('events')]] 318 319-- Map of event names to tables of handler functions. 320-- Handler tables are auto-created as needed. 321-- @class table 322-- @name handlers 323local handlers = setmetatable({}, {__index = function(t, k) 324 t[k] = {} 325 return t[k] 326end}) 327 328--- 329-- Adds function *f* to the set of event handlers for event *event* at position 330-- *index*. 331-- If *index* not given, appends *f* to the set of handlers. *event* may be any 332-- arbitrary string and does not need to have been previously defined. 333-- @param event The string event name. 334-- @param f The Lua function to connect to *event*. 335-- @param index Optional index to insert the handler into. 336-- @usage events.connect('my_event', function(msg) ui.print(msg) end) 337-- @see disconnect 338-- @name connect 339function M.connect(event, f, index) 340 assert_type(event, 'string', 1) 341 assert_type(f, 'function', 2) 342 assert_type(index, 'number/nil', 3) 343 M.disconnect(event, f) -- in case it already exists 344 table.insert(handlers[event], index or #handlers[event] + 1, f) 345end 346 347--- 348-- Removes function *f* from the set of handlers for event *event*. 349-- @param event The string event name. 350-- @param f The Lua function connected to *event*. 351-- @see connect 352-- @name disconnect 353function M.disconnect(event, f) 354 assert_type(f, 'function', 2) 355 for i = 1, #handlers[assert_type(event, 'string', 1)] do 356 if handlers[event][i] == f then table.remove(handlers[event], i) break end 357 end 358end 359 360local error_emitted = false 361--- 362-- Sequentially calls all handler functions for event *event* with the given 363-- arguments. 364-- *event* may be any arbitrary string and does not need to have been previously 365-- defined. If any handler explicitly returns a value that is not `nil`, 366-- `emit()` returns that value and ceases to call subsequent handlers. This is 367-- useful for stopping the propagation of an event like a keypress after it has 368-- been handled, or for passing back values from handlers. 369-- @param event The string event name. 370-- @param ... Arguments passed to the handler. 371-- @return `nil` unless any any handler explicitly returned a non-`nil` value; 372-- otherwise returns that value 373-- @usage events.emit('my_event', 'my message') 374-- @name emit 375function M.emit(event, ...) 376 local event_handlers = handlers[assert_type(event, 'string', 1)] 377 local i = 1 378 while i <= #event_handlers do 379 local handler = event_handlers[i] 380 local ok, result = pcall(handler, ...) 381 if not ok then 382 if not error_emitted then 383 error_emitted = true 384 M.emit(events.ERROR, result) 385 error_emitted = false 386 else 387 io.stderr:write(result) -- prevent infinite loop 388 end 389 end 390 if result ~= nil then return result end 391 if event_handlers[i] == handler then i = i + 1 end -- unless M.disconnect() 392 end 393end 394 395-- Handles Scintilla notifications. 396M.connect('SCN', function(notification) 397 local iface = _SCINTILLA.events[notification.code] 398 local args = {} 399 for i = 2, #iface do args[i - 1] = notification[iface[i]] end 400 return M.emit(iface[1], table.unpack(args)) 401end) 402 403-- Set event constants. 404for _, v in pairs(_SCINTILLA.events) do M[v[1]:upper()] = v[1] end 405local textadept_events = {'appleevent_odoc','buffer_after_switch','buffer_before_switch','buffer_deleted','buffer_new','csi','command_text_changed','error','find','find_text_changed','focus','initialized','keypress','menu_clicked','mouse','quit','replace','replace_all','reset_after','reset_before','resume','suspend', 'tab_clicked','unfocus','view_after_switch','view_before_switch','view_new'} 406for _, v in pairs(textadept_events) do M[v:upper()] = v end 407 408return M 409