diff options
-rw-r--r-- | core/ext/key_commands.lua | 199 | ||||
-rw-r--r-- | core/ext/keys.lua | 211 | ||||
-rw-r--r-- | init.lua | 3 |
3 files changed, 196 insertions, 217 deletions
diff --git a/core/ext/key_commands.lua b/core/ext/key_commands.lua index e0b9ae3a..1df0de40 100644 --- a/core/ext/key_commands.lua +++ b/core/ext/key_commands.lua @@ -4,7 +4,7 @@ local textadept = _G.textadept local locale = _G.locale --- --- Defines the key commands used by the Textadept key command manager. +-- Manages and defines key commands in Textadept. -- This set of key commands is pretty standard among other text editors. module('textadept.key_commands', package.seeall) @@ -64,6 +64,16 @@ module('textadept.key_commands', package.seeall) -- by setting the `keys.clear_sequence` field. Naturally, the clear sequence -- cannot be chained. -- +-- ## Settings +-- +-- * `SCOPES_ENABLED`: Flag indicating whether scopes/styles can be used for key +-- commands. +-- * `CTRL`: The string representing the Control key. +-- * `SHIFT`: The string representing the Shift key. +-- * `ALT`: The string representing the Alt key (the Apple key on Mac OSX). +-- * `ADD`: The string representing used to join together a sequence of Control, +-- Shift, or Alt modifier keys. +-- -- ## Key Command Precedence -- -- When searching for a key command to execute in the `keys` table, key commands @@ -106,6 +116,20 @@ module('textadept.key_commands', package.seeall) -- Windows and Linux key commands are listed in the first block. -- Mac OSX key commands are listed in the second block. +-- settings +local SCOPES_ENABLED = true +local ADD = '' +local CTRL = 'c'..ADD +local SHIFT = 's'..ADD +local ALT = 'a'..ADD +-- end settings + +--- +-- Global container that holds all key commands. +-- @class table +-- @name _G.keys +_G.keys = {} + local keys = _G.keys local b, v = 'buffer', 'view' local t = textadept @@ -461,7 +485,174 @@ end textadept.user_dofile('key_commands.lua') -- load user key commands +-- Do not edit below this line. + +-- optimize for speed +local string = _G.string +local string_char = string.char +local string_format = string.format +local pcall = _G.pcall +local ipairs = _G.ipairs +local next = _G.next +local type = _G.type +local unpack = _G.unpack +local MAC = _G.MAC + --- --- This module has no functions. -function no_functions() end -no_functions = nil -- undefine +-- Lookup table for key values higher than 255. +-- If a key value given to 'keypress' is higher than 255, this table is used to +-- return a string representation of the key if it exists. +-- @class table +-- @name KEYSYMS +KEYSYMS = { -- from <gdk/gdkkeysyms.h> + [65056] = '\t', -- backtab; will be 'shift'ed + [65288] = '\b', + [65289] = '\t', + [65293] = '\n', + [65307] = 'esc', + [65535] = 'del', + [65360] = 'home', + [65361] = 'left', + [65362] = 'up', + [65363] = 'right', + [65364] = 'down', + [65365] = 'pup', + [65366] = 'pdown', + [65367] = 'end', + [65379] = 'ins', + [65470] = 'f1', [65471] = 'f2', [65472] = 'f3', [65473] = 'f4', + [65474] = 'f5', [65475] = 'f6', [65476] = 'f7', [65477] = 'f8', + [65478] = 'f9', [65479] = 'f10', [65480] = 'f11', [65481] = 'f12', +} + +-- The current key sequence. +local keychain = {} + +-- Clears the current key sequence. +local function clear_key_sequence() + keychain = {} + textadept.statusbar_text = '' +end + +-- Helper function that gets commands associated with the current keychain from +-- 'keys'. +-- If the current item in the keychain is part of a chain, throw an error value +-- of -1. This way, pcall will return false and -1, where the -1 can easily and +-- efficiently be checked rather than using a string error message. +local function try_get_cmd(active_table) + for _, key_seq in ipairs(keychain) do active_table = active_table[key_seq] end + if #active_table == 0 and next(active_table) then + textadept.statusbar_text = locale.KEYCHAIN..table.concat(keychain, ' ') + error(-1, 0) + else + local func = active_table[1] + if type(func) == 'function' then + return func, { unpack(active_table, 2) } + elseif type(func) == 'string' then + local object = active_table[2] + if object == 'buffer' then + return buffer[func], { buffer, unpack(active_table, 3) } + elseif object == 'view' then + return view[func], { view, unpack(active_table, 3) } + end + else + error(locale.KEYS_UNKNOWN_COMMAND..tostring(func)) + end + end +end + +-- Tries to get a key command based on the lexer and current scope. +local function try_get_cmd1(keys, lexer, scope) + return try_get_cmd(keys[lexer][scope]) +end + +-- Tries to get a key command based on the lexer. +local function try_get_cmd2(keys, lexer) + return try_get_cmd(keys[lexer]) +end + +-- Tries to get a global key command. +local function try_get_cmd3(keys) + return try_get_cmd(keys) +end + +-- Handles Textadept keypresses. +-- It is called every time a key is pressed, and based on lexer and scope, +-- executes a command. The command is looked up in the global 'keys' key +-- command table. +-- @return whatever the executed command returns, true by default. A true +-- return value will tell Textadept not to handle the key afterwords. +local function keypress(code, shift, control, alt) + local buffer = buffer + local key + --print(code, string.char(code)) + if code < 256 then + key = string_char(code) + shift = false -- for printable characters, key is upper case + if MAC and not shift and not control and not alt then + local ch = string_char(code) + -- work around native GTK-OSX's handling of Alt key + if ch:find('[%p%d]') and #keychain == 0 then + if buffer.anchor ~= buffer.current_pos then buffer:delete_back() end + buffer:add_text(ch) + textadept.events.handle('char_added', code) + return true + end + end + else + if not KEYSYMS[code] then return end + key = KEYSYMS[code] + end + control = control and CTRL or '' + shift = shift and SHIFT or '' + alt = alt and ALT or '' + local key_seq = string_format('%s%s%s%s', control, shift, alt, key) + + if #keychain > 0 and key_seq == keys.clear_sequence then + clear_key_sequence() + return true + end + + local lexer = buffer:get_lexer_language() + keychain[#keychain + 1] = key_seq + local ret, func, args + if SCOPES_ENABLED then + local style = buffer.style_at[buffer.current_pos] + local scope = buffer:get_style_name(style) + --print(key_seq, 'Lexer: '..lexer, 'Scope: '..scope) + ret, func, args = pcall(try_get_cmd1, keys, lexer, scope) + end + if not ret and func ~= -1 then + ret, func, args = pcall(try_get_cmd2, keys, lexer) + end + if not ret and func ~= -1 then + ret, func, args = pcall(try_get_cmd3, keys) + end + + if ret then + clear_key_sequence() + if type(func) == 'function' then + local ret, retval = pcall(func, unpack(args)) + if ret then + if type(retval) == 'boolean' then return retval end + else + error(retval) + end + end + return true + else + -- Clear key sequence because it's not part of a chain. + -- (try_get_cmd throws error number -1.) + if func ~= -1 then + local size = #keychain - 1 + clear_key_sequence() + if size > 0 then -- previously in a chain + textadept.statusbar_text = locale.KEYS_INVALID + return true + end + else + return true + end + end +end +textadept.events.add_handler('keypress', keypress, 1) diff --git a/core/ext/keys.lua b/core/ext/keys.lua deleted file mode 100644 index e5066f17..00000000 --- a/core/ext/keys.lua +++ /dev/null @@ -1,211 +0,0 @@ --- Copyright 2007-2010 Mitchell mitchell<att>caladbolg.net. See LICENSE. - -local textadept = _G.textadept -local locale = _G.locale - ---- --- Manages key commands in Textadept. --- Default key commands should be defined in a separate file and loaded after --- all modules. -module('textadept.keys', package.seeall) - --- Markdown: --- ## Settings --- --- * `SCOPES_ENABLED`: Flag indicating whether scopes/styles can be used for key --- commands. --- * `CTRL`: The string representing the Control key. --- * `SHIFT`: The string representing the Shift key. --- * `ALT`: The string representing the Alt key (the Apple key on Mac OSX). --- * `ADD`: The string representing used to join together a sequence of Control, --- Shift, or Alt modifier keys. --- - --- settings -local SCOPES_ENABLED = true -local ADD = '' -local CTRL = 'c'..ADD -local SHIFT = 's'..ADD -local ALT = 'a'..ADD --- end settings - ---- --- Global container that holds all key commands. --- @class table --- @name _G.keys -_G.keys = {} - --- optimize for speed -local keys = _G.keys -local string = _G.string -local string_char = string.char -local string_format = string.format -local pcall = _G.pcall -local ipairs = _G.ipairs -local next = _G.next -local type = _G.type -local unpack = _G.unpack -local MAC = _G.MAC - ---- --- Lookup table for key values higher than 255. --- If a key value given to 'keypress' is higher than 255, this table is used to --- return a string representation of the key if it exists. --- @class table --- @name KEYSYMS -KEYSYMS = { -- from <gdk/gdkkeysyms.h> - [65056] = '\t', -- backtab; will be 'shift'ed - [65288] = '\b', - [65289] = '\t', - [65293] = '\n', - [65307] = 'esc', - [65535] = 'del', - [65360] = 'home', - [65361] = 'left', - [65362] = 'up', - [65363] = 'right', - [65364] = 'down', - [65365] = 'pup', - [65366] = 'pdown', - [65367] = 'end', - [65379] = 'ins', - [65470] = 'f1', [65471] = 'f2', [65472] = 'f3', [65473] = 'f4', - [65474] = 'f5', [65475] = 'f6', [65476] = 'f7', [65477] = 'f8', - [65478] = 'f9', [65479] = 'f10', [65480] = 'f11', [65481] = 'f12', -} - --- The current key sequence. -local keychain = {} - --- local functions -local try_get_cmd1, try_get_cmd2, try_get_cmd3, try_get_cmd - ---- --- Clears the current key sequence. -function clear_key_sequence() - keychain = {} - textadept.statusbar_text = '' -end - --- Handles Textadept keypresses. --- It is called every time a key is pressed, and based on lexer and scope, --- executes a command. The command is looked up in the global 'keys' key --- command table. --- @return whatever the executed command returns, true by default. A true --- return value will tell Textadept not to handle the key afterwords. -local function keypress(code, shift, control, alt) - local buffer = buffer - local key - --print(code, string.char(code)) - if code < 256 then - key = string_char(code) - shift = false -- for printable characters, key is upper case - if MAC and not shift and not control and not alt then - local ch = string_char(code) - -- work around native GTK-OSX's handling of Alt key - if ch:find('[%p%d]') and #keychain == 0 then - if buffer.anchor ~= buffer.current_pos then buffer:delete_back() end - buffer:add_text(ch) - textadept.events.handle('char_added', code) - return true - end - end - else - if not KEYSYMS[code] then return end - key = KEYSYMS[code] - end - control = control and CTRL or '' - shift = shift and SHIFT or '' - alt = alt and ALT or '' - local key_seq = string_format('%s%s%s%s', control, shift, alt, key) - - if #keychain > 0 and key_seq == keys.clear_sequence then - clear_key_sequence() - return true - end - - local lexer = buffer:get_lexer_language() - keychain[#keychain + 1] = key_seq - local ret, func, args - if SCOPES_ENABLED then - local style = buffer.style_at[buffer.current_pos] - local scope = buffer:get_style_name(style) - --print(key_seq, 'Lexer: '..lexer, 'Scope: '..scope) - ret, func, args = pcall(try_get_cmd1, keys, lexer, scope) - end - if not ret and func ~= -1 then - ret, func, args = pcall(try_get_cmd2, keys, lexer) - end - if not ret and func ~= -1 then - ret, func, args = pcall(try_get_cmd3, keys) - end - - if ret then - clear_key_sequence() - if type(func) == 'function' then - local ret, retval = pcall(func, unpack(args)) - if ret then - if type(retval) == 'boolean' then return retval end - else - error(retval) - end - end - return true - else - -- Clear key sequence because it's not part of a chain. - -- (try_get_cmd throws error number -1.) - if func ~= -1 then - local size = #keychain - 1 - clear_key_sequence() - if size > 0 then -- previously in a chain - textadept.statusbar_text = locale.KEYS_INVALID - return true - end - else - return true - end - end -end -textadept.events.add_handler('keypress', keypress, 1) - --- Tries to get a key command based on the lexer and current scope. -try_get_cmd1 = function(keys, lexer, scope) - return try_get_cmd(keys[lexer][scope]) -end - --- Tries to get a key command based on the lexer. -try_get_cmd2 = function(keys, lexer) - return try_get_cmd(keys[lexer]) -end - --- Tries to get a global key command. -try_get_cmd3 = function(keys) - return try_get_cmd(keys) -end - --- Helper function that gets commands associated with the current keychain from --- 'keys'. --- If the current item in the keychain is part of a chain, throw an error value --- of -1. This way, pcall will return false and -1, where the -1 can easily and --- efficiently be checked rather than using a string error message. -try_get_cmd = function(active_table) - for _, key_seq in ipairs(keychain) do active_table = active_table[key_seq] end - if #active_table == 0 and next(active_table) then - textadept.statusbar_text = locale.KEYCHAIN..table.concat(keychain, ' ') - error(-1, 0) - else - local func = active_table[1] - if type(func) == 'function' then - return func, { unpack(active_table, 2) } - elseif type(func) == 'string' then - local object = active_table[2] - if object == 'buffer' then - return buffer[func], { buffer, unpack(active_table, 3) } - elseif object == 'view' then - return view[func], { view, unpack(active_table, 3) } - end - else - error(locale.KEYS_UNKNOWN_COMMAND..tostring(func)) - end - end -end @@ -14,7 +14,6 @@ package.path = table.concat(paths, ';') if not textadept.user_dofile('init.lua') then -- Core extension modules to load on startup. -require 'ext/keys' -- provides key command support require 'ext/find' -- provides functionality for find/replace require 'ext/command_entry' -- provides tab-completion for the command entry require 'ext/mime_types' -- provides support for language detection based on @@ -26,7 +25,7 @@ require 'textadept' -- Core extension modules that must be loaded last. require 'ext/menu' -- provides the menu bar -require 'ext/key_commands' -- key commands +require 'ext/key_commands' -- provides key command support end if not RESETTING then |