aboutsummaryrefslogtreecommitdiffhomepage
path: root/core/keys.lua
diff options
context:
space:
mode:
authorGravatar mitchell <70453897+667e-11@users.noreply.github.com>2013-04-11 23:58:33 -0400
committerGravatar mitchell <70453897+667e-11@users.noreply.github.com>2013-04-11 23:58:33 -0400
commita65f9ac0c460fba64b2d8ddde2149661c025ea65 (patch)
tree9f1d3e7b73756b264974d0be272a722fc92e483e /core/keys.lua
parent2a54e9fcfd955b8cf63fa42f41292d1176026721 (diff)
Added key modes and changed the command entry to use them.
Removed obsoleted `events.COMMAND_ENTRY_COMMAND`.
Diffstat (limited to 'core/keys.lua')
-rw-r--r--core/keys.lua122
1 files changed, 73 insertions, 49 deletions
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