From a65f9ac0c460fba64b2d8ddde2149661c025ea65 Mon Sep 17 00:00:00 2001 From: mitchell <70453897+667e-11@users.noreply.github.com> Date: Thu, 11 Apr 2013 23:58:33 -0400 Subject: Added key modes and changed the command entry to use them. Removed obsoleted `events.COMMAND_ENTRY_COMMAND`. --- core/keys.lua | 122 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 49 deletions(-) (limited to 'core/keys.lua') diff --git a/core/keys.lua b/core/keys.lua index f1bfb7d1..794c3001 100644 --- a/core/keys.lua +++ b/core/keys.lua @@ -11,13 +11,15 @@ local M = {} -- Key bindings are defined in the global table `keys`. Each key-value pair in -- `keys` consists of either a string key sequence and its associated command, -- a string lexer language (from the *lexers/* directory) with a table of key --- sequences and commands, or a key sequence with a table of more sequences and --- commands. The latter is part of what is called a "key chain". When searching --- for a command to run based on a key sequence, key bindings in the current --- lexer have priority, followed by the ones in the global table. This means if --- there are two commands with the same key sequence, the one specific to the --- current lexer is run. However, if the command returns the boolean value --- `false`, the lower-priority command is also run. (This is useful for +-- sequences and commands, a string key mode with a table of key sequences and +-- commands, or a key sequence with a table of more sequences and commands. The +-- latter is part of what is called a "key chain", to be discussed below. When +-- searching for a command to run based on a key sequence, key bindings in the +-- current key mode have priority. If no key mode is active, key bindings in the +-- current lexer have priority, followed by the ones in the global table. This +-- means if there are two commands with the same key sequence, the one specific +-- to the current lexer is run. However, if the command returns the boolean +-- value `false`, the lower-priority command is also run. (This is useful for -- language-specific modules to override commands like Adeptsense -- autocompletion, but fall back to word autocompletion if the first command -- fails.) @@ -52,18 +54,45 @@ local M = {} -- -- ## Commands -- --- Commands associated with key sequences can be either Lua functions, or --- tables containing Lua functions with a set of arguments to call the function --- with. Examples are: +-- Commands bound to key sequences can be either Lua functions, or tables +-- containing Lua functions with a set of arguments to call the function with. +-- Examples are: -- -- keys['cn'] = new_buffer -- keys['cs'] = buffer.save -- keys['a('] = {_M.textadept.editing.enclose, '(', ')'} +-- keys['cu'] = function() io.snapopen(_USERHOME) end -- --- Note that [`buffer`][] references are handled properly. +-- (The function and function table syntax are functionally equivalent. You can +-- use either.) +-- +-- [`buffer`][] references are handled properly in static contexts. -- -- [`buffer`]: buffer.html -- +-- ## Modes +-- +-- Sets of key bindings can be grouped together into modes. When a key +-- [mode](#MODE) is active, all key bindings defined outside the mode are +-- ignored until the mode is unset. Here is a simple vi mode example: +-- +-- keys.command_mode = { +-- ['h'] = buffer.char_left, +-- ['j'] = buffer.line_up, +-- ['k'] = buffer.line_down, +-- ['l'] = buffer.char_right, +-- ['i'] = function() +-- keys.MODE = nil +-- gui.statusbar_text = 'INSERT MODE' +-- end +-- } +-- keys['esc'] = function() keys.MODE = 'command_mode' end +-- events.connect(events.UPDATE_UI, function() +-- if keys.MODE == 'command_mode' then return end +-- gui.statusbar_text = 'INSERT MODE' +-- end) +-- keys.MODE = 'command_mode' -- default mode +-- -- ## Key Chains -- -- Key chains are a powerful concept. They allow multiple key bindings to be @@ -86,6 +115,11 @@ local M = {} -- The starting key of the key chain reserved for language-specific modules. -- The default value is `'cl'` on platforms other than Mac OSX, `'ml'` -- otherwise. Equivalent to `Ctrl+L` (`⌘L` on Mac OSX | `M-L` in curses). +-- @field MODE (string) +-- The current key mode. +-- When non-`nil`, all key bindings defined outside of `keys[MODE]` are +-- ignored. +-- The default value is `nil`. module('keys')]] local ADD = '' @@ -112,9 +146,9 @@ local error = function(e) events.emit(events.ERROR, e) end -- @name KEYSYMS M.KEYSYMS = { -- From Scintilla.h and cdk/curdefs.h. - [7] = 'esc', [8] = '\b', [9] = '\t', [13] = '\n', [27] = 'esc', [127] = 'del', + [7] = 'esc', -- From curses.h. - [263] = '\b', + [263] = '\b', [343] = '\n', -- From Scintilla.h. [300] = 'down', [301] = 'up', [302] = 'left', [303] = 'right', [304] = 'home', [305] = 'end', @@ -160,6 +194,8 @@ local function clear_key_sequence() -- Clearing a table is faster than re-creating one. if #keychain == 1 then keychain[1] = nil else keychain = {} end end +-- Export for command_entry.lua without creating LuaDoc. +if CURSES then M.clear_key_sequence = clear_key_sequence end -- Runs a given command. -- This is also used by *modules/textadept/menu.lua*. @@ -185,36 +221,30 @@ local function run_command(command, command_type) end M.run_command = run_command -- export for menu.lua without creating LuaDoc --- Return codes for `run_key_command()`. +-- Return codes for `key_command()`. local INVALID, PROPAGATE, CHAIN, HALT = -1, 0, 1, 2 -- Runs a key command associated with the current keychain. --- @param lexer Optional lexer name for lexer-specific commands. +-- @param prefix Optional prefix name for mode/lexer-specific commands. -- @return `INVALID`, `PROPAGATE`, `CHAIN`, or `HALT`. -local function run_key_command(lexer) - local key, key_type = keys, type(keys) - if lexer and key_type == 'table' and key[lexer] then key = key[lexer] end - if type(key) ~= 'table' then return INVALID end - - key = key[keychain[1]] - for i = 2, #keychain do +local function key_command(prefix) + local key = not prefix and M or M[prefix] + for i = 1, #keychain do if type(key) ~= 'table' then return INVALID end key = key[keychain[i]] end - key_type = type(key) - + local key_type = type(key) if key_type ~= 'function' and key_type ~= 'table' then return INVALID end if key_type == 'table' and #key == 0 and next(key) then gui.statusbar_text = _L['Keychain:']..' '..table.concat(keychain, ' ') return CHAIN end - return run_command(key, key_type) == false and PROPAGATE or HALT end -- Handles Textadept keypresses. --- It is called every time a key is pressed, and based on lexer, executes a --- command. The command is looked up in the `_G.keys` table. +-- It is called every time a key is pressed, and based on a mode or lexer, +-- executes a command. The command is looked up in the `_G.keys` table. -- @param code The keycode. -- @param shift Whether or not the Shift modifier is pressed. -- @param control Whether or not the Control modifier is pressed. @@ -222,40 +252,34 @@ end -- @param meta Whether or not the Command modifier on Mac OSX is pressed. -- @return `true` to stop handling the key; `nil` otherwise. local function keypress(code, shift, control, alt, meta) - local buffer = buffer - local key --print(code, M.KEYSYMS[code], shift, control, alt, meta) - if code < 256 then - key = (not CURSES or code ~= 7) and string_char(code) or M.KEYSYMS[code] - shift = shift and code < 32 -- for printable characters, key is upper case - else - key = M.KEYSYMS[code] - if not key then return end - end + local key = code < 256 and (not CURSES or code ~= 7) and string_char(code) or + M.KEYSYMS[code] + if not key then return end + shift = shift and (code >= 256 or code == 9) -- printable chars are uppercased local key_seq = (control and CTRL or '')..(alt and ALT or '').. (meta and OSX and META or '')..(shift and SHIFT or '')..key --print(key_seq) gui.statusbar_text = '' --if CURSES then gui.statusbar_text = '"'..key_seq..'"' end - if #keychain > 0 and key_seq == M.CLEAR then + local keychain_size = #keychain + if keychain_size > 0 and key_seq == M.CLEAR then clear_key_sequence() return true end - keychain[#keychain + 1] = key_seq + keychain[keychain_size + 1] = key_seq - local success - for i = 1, 2 do - local status = run_key_command(i == 1 and buffer:get_lexer(true)) - if status > 0 then -- CHAIN or HALT - if status == HALT then clear_key_sequence() end - return true - end - success = success or status ~= -1 + local status = PROPAGATE + if not M.MODE then + status = key_command(buffer:get_lexer(true)) + if status <= PROPAGATE and not M.MODE then status = key_command() end + else + status = key_command(M.MODE) end - local size = #keychain - clear_key_sequence() - if not success and size > 1 then -- INVALID keychain sequence + if status ~= CHAIN then clear_key_sequence() end + if status > PROPAGATE then return true end -- CHAIN or HALT + if status == INVALID and keychain_size > 0 then gui.statusbar_text = _L['Invalid sequence'] return true end -- cgit v1.2.3