aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar mitchell <70453897+667e-11@users.noreply.github.com>2020-06-28 00:15:04 -0400
committerGravatar mitchell <70453897+667e-11@users.noreply.github.com>2020-06-28 00:15:04 -0400
commit8e30adbb7886b61a680d0d01bdd50c29a015ee79 (patch)
tree47dbf1d9c96379c931215175c01480097ff448ca
parenta96fa9e5c2f255a938bd15373344a16448b255c7 (diff)
Changed keybinding modifier keys.
They had always been a bit counter-intuitive.
-rw-r--r--core/._M.luadoc2
-rw-r--r--core/keys.lua52
-rw-r--r--doc/manual.md24
-rw-r--r--init.lua1
-rw-r--r--modules/textadept/command_entry.lua41
-rw-r--r--modules/textadept/find.lua6
-rw-r--r--modules/textadept/keys.lua578
-rw-r--r--modules/textadept/menu.lua17
-rw-r--r--test/test.lua8
9 files changed, 358 insertions, 371 deletions
diff --git a/core/._M.luadoc b/core/._M.luadoc
index 479b88bb..20ea1c01 100644
--- a/core/._M.luadoc
+++ b/core/._M.luadoc
@@ -114,7 +114,7 @@
-- existing modules and it will not conflict with any default key bindings.
-- For example:
--
--- keys.lua[not OSX and not CURSES and 'cl' or 'ml'] = {
+-- keys.lua[CURSES and 'meta+l' or OSX and 'cmd+l' or 'ctrl+l'] = {
-- ...
-- }
--
diff --git a/core/keys.lua b/core/keys.lua
index 76de277f..2b93730b 100644
--- a/core/keys.lua
+++ b/core/keys.lua
@@ -32,33 +32,34 @@ local M = {}
-- (`^`), "Alt/Option" (`⌥`), "Command" (`⌘`), and "Shift" (`⇧`). These
-- modifiers have the following string representations:
--
--- Modifier | Linux / Win32 | Mac OSX | curses |
--- ---------|---------------|---------|----------|
--- Control | `'c'` | `'c'` | `'c'` |
--- Alt | `'a'` | `'a'` | `'m'` |
--- Command | N/A | `'m'` | N/A |
--- Shift | `'s'` | `'s'` | `'s'` |
+-- Modifier | Linux / Win32 | Mac OSX | curses |
+-- ---------|---------------|-----------|-----------|
+-- Control | `'ctrl' | `'ctrl'` | `'ctrl'` |
+-- Alt | `'alt'` | `'alt'` | `'meta'` |
+-- Command | N/A | `'cmd'` | N/A |
+-- Shift | `'shift'` | `'shift'` | `'shift'` |
--
-- The string representation of key values less than 255 is the character that
-- Textadept would normally insert if the "Control", "Alt", and "Command"
-- modifiers were not held down. Therefore, a combination of `Ctrl+Alt+Shift+A`
--- has the key sequence `caA` on Windows and Linux, but a combination of
--- `Ctrl+Shift+Tab` has the key sequence `cs\t`. On a United States English
--- keyboard, since the combination of `Ctrl+Shift+,` has the key sequence `c<`
--- (`Shift+,` inserts a `<`), Textadept recognizes the key binding as `Ctrl+<`.
--- This allows key bindings to be language and layout agnostic. For key values
--- greater than 255, Textadept uses the [`keys.KEYSYMS`]() lookup table.
--- Therefore, `Ctrl+Right Arrow` has the key sequence `cright`. Uncommenting the
--- `print()` statements in *core/keys.lua* causes Textadept to print key
--- sequences to standard out (stdout) for inspection.
+-- has the key sequence `ctrl+alt+A` on Windows and Linux, but a combination of
+-- `Ctrl+Shift+Tab` has the key sequence `ctrl+shift+\t`. On a United States
+-- English keyboard, since the combination of `Ctrl+Shift+,` has the key
+-- sequence `ctrl+<` (`Shift+,` inserts a `<`), Textadept recognizes the key
+-- binding as `Ctrl+<`. This allows key bindings to be language and layout
+-- agnostic. For key values greater than 255, Textadept uses the
+-- [`keys.KEYSYMS`]() lookup table. Therefore, `Ctrl+Right Arrow` has the key
+-- sequence `ctrl+right`. Uncommenting the `print()` statements in
+-- *core/keys.lua* causes Textadept to print key sequences to standard out
+-- (stdout) for inspection.
--
-- ## Commands
--
-- A command bound to a key sequence is simply a Lua function. For example:
--
--- keys['cn'] = buffer.new
--- keys['cz'] = buffer.undo
--- keys['cu'] = function() io.quick_open(_USERHOME) end
+-- keys['ctrl+n'] = buffer.new
+-- keys['ctrl+z'] = buffer.undo
+-- keys['ctrl+u'] = function() io.quick_open(_USERHOME) end
--
-- Textadept handles [`buffer`]() references properly in static contexts.
--
@@ -95,7 +96,7 @@ local M = {}
-- but you can redefine it via [`keys.CLEAR`](). An example key chain looks
-- like:
--
--- keys['aa'] = {
+-- keys['alt+a'] = {
-- a = function1,
-- b = function2,
-- c = {...}
@@ -111,7 +112,8 @@ local M = {}
-- The default value is `nil`.
module('keys')]]
-local CTRL, ALT, META, SHIFT = 'c', not CURSES and 'a' or 'm', 'm', 's'
+local CTRL, ALT, SHIFT = 'ctrl+', not CURSES and 'alt+' or 'meta+', 'shift+'
+local CMD = 'cmd+'
M.CLEAR = 'esc'
---
@@ -198,12 +200,12 @@ end
-- @param shift Whether or not the Shift modifier is pressed.
-- @param control Whether or not the Control modifier is pressed.
-- @param alt Whether or not the Alt/option modifier is pressed.
--- @param meta Whether or not the Command modifier on Mac OSX is pressed.
+-- @param cmd Whether or not the Command modifier on Mac OSX is pressed.
-- @param caps_lock Whether or not Caps Lock is enabled.
-- @return `true` to stop handling the key; `nil` otherwise.
-local function keypress(code, shift, control, alt, meta, caps_lock)
- --print(code, M.KEYSYMS[code], shift, control, alt, meta, caps_lock)
- if caps_lock and (shift or control or alt or meta) and code < 256 then
+local function keypress(code, shift, control, alt, cmd, caps_lock)
+ --print(code, M.KEYSYMS[code], shift, control, alt, cmd, caps_lock)
+ if caps_lock and (shift or control or alt or cmd) and code < 256 then
code = string[shift and 'upper' or 'lower'](string.char(code)):byte()
end
local key = code >= 32 and code < 256 and string.char(code) or M.KEYSYMS[code]
@@ -213,7 +215,7 @@ local function keypress(code, shift, control, alt, meta, caps_lock)
-- For composed keys on OSX, ignore alt.
if OSX and alt and code < 256 then alt = false end
local key_seq = (control and CTRL or '') .. (alt and ALT or '') ..
- (meta and OSX and META or '') .. (shift and SHIFT or '') .. key
+ (cmd and OSX and CMD or '') .. (shift and SHIFT or '') .. key
--print(key_seq)
ui.statusbar_text = ''
diff --git a/doc/manual.md b/doc/manual.md
index d78bec27..75e3e70e 100644
--- a/doc/manual.md
+++ b/doc/manual.md
@@ -1125,19 +1125,19 @@ creates a new buffer instead of `Ctrl+N`, or that the buffer list (`Ctrl+B`)
shows buffers by their z-order (most recently viewed to least recently viewed)
instead of the order they were opened in:
- keys.cC = buffer.new
- keys.cn = nil
- keys.cb = function() ui.switch_buffer(true) end
+ keys['ctrl+C'] = buffer.new
+ keys['ctrl+n'] = nil
+ keys['ctrl+b'] = function() ui.switch_buffer(true) end
A key binding is simply a Lua function assigned to a key sequence in the global
`keys` table. Key sequences are composed of an ordered combination of modifier
keys followed by either the key's *inserted character*, or if no such character
exists, the string representation of the key. On Windows and Linux, modifier
-keys are "Control", "Alt", and "Shift", represented by `c`, `a`, and `s`,
-respectively. On Mac OSX, modifier keys are "Control", "Alt/Option", "Command",
-and "Shift", represented by `c`, `a`, `m`, and `s`, respectively. On curses,
-modifier keys are "Control", "Alt", and "Shift", represented by `c`, `m` (for
-Meta), and `s`, respectively.
+keys are "Control", "Alt", and "Shift", represented by `ctrl`, `alt`, and
+`shift`, respectively. On Mac OSX, modifier keys are "Control", "Alt/Option",
+"Command", and "Shift", represented by `ctrl`, `alt`, `cmd`, and `shift`,
+respectively. On curses, modifier keys are "Control", "Alt", and "Shift",
+represented by `ctrl`, `meta`, and `shift`, respectively.
Key bindings can also be language-specific by storing them in a
`keys[`*lexer*`]` table. If you wanted to add or modify language-specific key
@@ -1147,7 +1147,7 @@ bindings outside of a language module, you would add something like this to
events.connect(events.LEXER_LOADED, function(lexer)
if lexer ~= '...' then return end
if not keys[lexer] then keys[lexer] = {} end
- keys[lexer].cn = function() ... end
+ keys[lexer]['ctrl+n'] = function() ... end
end)
If you plan on redefining most key bindings (e.g. in order to mimic an editor
@@ -2152,6 +2152,12 @@ examples.
#### Key Bindings Changes
+Key binding modifiers have changed from their shortened form to a longer form
+that is more intuitive. `'c'` is now `'ctrl'`, `'a'` is now `'alt'`, `'m'` is
+now `'cmd'` on Mac OSX and `'meta'` in the terminal version, and `'s'` is now
+`'shift'`. For example, `keys.cn = ...` is now `keys['ctrl+n'] = ...` and
+`keys['m<'] = ...` is now `keys['cmd+<'] = ...` or `keys['meta+<'] = ...`.
+
The key binding for inserting a user-specified snippet from a dialog has changed
from `Ctrl+K` (`⌥⇥` on Mac OSX | `M-K` on curses) to `Ctrl+Shift+K`
(`⌥⇧⇥` | `M-S-K`). `Ctrl+K` (`⌥⇥` | `M-K`) now autocompletes snippet names.
diff --git a/init.lua b/init.lua
index e8aaed64..6cb85003 100644
--- a/init.lua
+++ b/init.lua
@@ -49,6 +49,7 @@ setmetatable(_L, {__index = function(t, k) return rawget(t, k:gsub('_', '')) or
setmetatable(textadept.snippets, {__index = function(t, k) return rawget(t, k:gsub('^_', '')) end})
buffer.set_theme = function(...) view:set_theme(select(2, ...)); events.connect(events.INITIALIZED, function() ui.dialogs.msgbox{title='Compatibility issue',text='Please change your use of "buffer:set_theme()" to "view:set_theme()"'} end) end
local function en_au_to_us() for au,us in pairs{CASEINSENSITIVEBEHAVIOUR_IGNORECASE=buffer.CASEINSENSITIVEBEHAVIOR_IGNORECASE,CASEINSENSITIVEBEHAVIOUR_RESPECTCASE=buffer.CASEINSENSITIVEBEHAVIOR_RESPECTCASE,INDIC_GRADIENTCENTRE=buffer.INDIC_GRADIENTCENTER,MARGIN_COLOUR=buffer.MARGIN_COLOR,auto_c_case_insensitive_behaviour=buffer.auto_c_case_insensitive_behavior,colourise=buffer.colorize,edge_colour=buffer.edge_color,set_fold_margin_colour=function()ui.dialogs.msgbox{text='Compatibility issue',text="Please update your theme's use of renamed buffer/view fields"}; return buffer.set_fold_margin_color end,set_fold_margin_hi_colour=buffer.set_fold_margin_hi_color,vertical_centre_caret=buffer.vertical_center_caret} do buffer[au]=us;view[au]=us end end; events.connect(events.BUFFER_NEW, en_au_to_us); en_au_to_us()
+--events.connect(events.INITIALIZED, function() local update_keys={}; local function translate_keys(keys,new_keys) for k,v in pairs(keys) do if type(k)=='string' and k:find('^[cmas]+.') and not k:find('ctrl') and not k:find('cmd') and not k:find('alt') and not k:find('meta') and not k:find('shift') then update_keys[#update_keys+1]=k; k=k:gsub('^(c?m?a?)s(.)','%1shift+%2'):gsub('^(c?m?)a(.)','%1alt+%2'):gsub('^(c?)m(.)',string.format('%%1%s%%2',OSX and 'cmd+' or 'meta+')):gsub('^c(.)','ctrl+%1') end new_keys[k]=type(v)=='table' and translate_keys(v,{}) or v end return new_keys end; for k,v in pairs(translate_keys(keys,{})) do keys[k]=v end; if #update_keys>0 then ui.dialogs.msgbox{text='Compatibility issue',text='Please update your keys to use the new modifiers:\n'..table.concat(update_keys,'\n')} end end)
-- The remainder of this file defines default buffer properties and applies them
-- to subsequent buffers. Normally, a setting like `buffer.use_tabs = false`
diff --git a/modules/textadept/command_entry.lua b/modules/textadept/command_entry.lua
index 001dfa14..eb939137 100644
--- a/modules/textadept/command_entry.lua
+++ b/modules/textadept/command_entry.lua
@@ -41,26 +41,33 @@ end
-- @usage setmetatable(mode_keys, ui.command_entry.editing_keys)
-- @class table
-- @name editing_keys
-M.editing_keys = {__index = {
+M.editing_keys = {__index = {}}
+
+-- Fill in default platform-specific key bindings.
+local ekeys, plat = M.editing_keys.__index, CURSES and 3 or OSX and 2 or 1
+local bindings = {
-- Note: cannot use `M.cut`, `M.copy`, etc. since M is never considered the
-- global buffer.
- [not OSX and 'cx' or 'mx'] = function() M:cut() end,
- [not OSX and 'cc' or 'mc'] = function() M:copy() end,
- [not OSX and 'cv' or 'mv'] = function() M:paste() end,
- [not OSX and not CURSES and 'ca' or 'ma'] = function() M:select_all() end,
- [not OSX and 'cz' or 'mz'] = function() M:undo() end,
- [not OSX and 'cZ' or 'mZ'] = function() M:redo() end,
- [not OSX and 'cy' or '\0'] = function() M:redo() end,
- up = function() cycle_history(true) end, down = cycle_history,
- [(OSX or CURSES) and 'cp' or '\0'] = function() cycle_history(true) end,
- [(OSX or CURSES) and 'cn' or '\0'] = cycle_history,
+ [function() M:undo() end] = {'ctrl+z', 'cmd+z', 'ctrl+z'},
+ [function() M:undo() end] = {nil, nil, 'meta+z'},
+ [function() M:redo() end] = {'ctrl+y', 'cmd+Z', 'ctrl+y'},
+ [function() M:redo() end] = {'ctrl+Z', nil, 'meta+Z'},
+ [function() M:cut() end] = {'ctrl+x', 'cmd+x', 'ctrl+x'},
+ [function() M:copy() end] = {'ctrl+c', 'cmd+c', 'ctrl+c'},
+ [function() M:paste() end] = {'ctrl+v', 'cmd+v', 'ctrl+v'},
+ [function() M:select_all() end] = {'ctrl+a', 'cmd+a', 'meta+a'},
+ [function() cycle_history(true) end] = {'up', 'up', 'up'},
+ [cycle_history] = {'down', 'down', 'down'},
-- Movement keys.
- [(OSX or CURSES) and 'cf' or '\0'] = function() M:char_right() end,
- [(OSX or CURSES) and 'cb' or '\0'] = function() M:char_left() end,
- [(OSX or CURSES) and 'ca' or '\0'] = function() M:vc_home() end,
- [(OSX or CURSES) and 'ce' or '\0'] = function() M:line_end() end,
- [(OSX or CURSES) and 'cd' or '\0'] = function() M:clear() end
-}}
+ [function() M:char_right() end] = {nil, 'ctrl+f', 'ctrl+f'},
+ [function() M:char_left() end] = {nil, 'ctrl+b', 'ctrl+b'},
+ [function() M:vc_home() end] = {nil, 'ctrl+a', 'ctrl+a'},
+ [function() M:line_end() end] = {nil, 'ctrl+e', 'ctrl+e'},
+ [function() M:clear() end] = {nil, 'ctrl+d', 'ctrl+d'}
+}
+for f, plat_keys in pairs(bindings) do
+ if plat_keys[plat] then ekeys[plat_keys[plat]] = f end
+end
-- Environment for abbreviated Lua commands.
-- @class table
diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua
index e04bf786..51abda3f 100644
--- a/modules/textadept/find.lua
+++ b/modules/textadept/find.lua
@@ -220,8 +220,10 @@ M.find_incremental_keys = setmetatable({
}, {__index = function(_, k)
-- Add the character for any key pressed without modifiers to incremental
-- find.
- if #k > 1 and k:find('^[cams]*.+$') then return end
- M.find_incremental(ui.command_entry:get_text() .. k, true)
+ if #k == 1 or not k:find('ctrl%+') and not k:find('alt%+') and
+ not k:find('meta%+') and not k:find('shift%+') then
+ M.find_incremental(ui.command_entry:get_text() .. k, true)
+ end
end})
---
diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua
index 87f20c1f..70b5ca19 100644
--- a/modules/textadept/keys.lua
+++ b/modules/textadept/keys.lua
@@ -19,6 +19,7 @@ local M = {}
-- Ctrl+Shift+O |⌘⇧O |M-O |Reload file
-- Ctrl+S |⌘S |^S |Save file
-- Ctrl+Shift+S |⌘⇧S |M-^S |Save file as..
+-- None |None |None |Save all files
-- Ctrl+W |⌘W |^W |Close file
-- Ctrl+Shift+W |⌘⇧W |M-^W |Close all files
-- None |None |None |Load session...
@@ -80,25 +81,24 @@ local M = {}
-- Ctrl+Shift+X |⌘⇧X |M-^X |Stop
-- Ctrl+Alt+E |^⌘E |M-X |Next Error
-- Ctrl+Alt+Shift+E|^⌘⇧E |M-S-X |Previous Error
--- Ctrl+Space |⌥Esc |^Space |Complete symbol
--- Ctrl+H |^H |M-H<br/>M-S-H|Show documentation
--- Tab |⇥ |Tab |Expand snippet or next placeholder
--- Ctrl+Shift+K |⌥⇧⇥ |M-S-K |Insert snippet...
--- Shift+Tab |⇧⇥ |S-Tab |Previous snippet placeholder
--- Esc |Esc |Esc |Cancel snippet
--- Ctrl+K |⌥⇥ |M-K |Complete trigger word
-- Ctrl+F2 |⌘F2 |F1 |Toggle bookmark
-- Ctrl+Shift+F2 |⌘⇧F2 |F6 |Clear bookmarks
-- F2 |F2 |F2 |Next bookmark
-- Shift+F2 |⇧F2 |F3 |Previous bookmark
-- Alt+F2 |⌥F2 |F4 |Goto bookmark...
--- F9 |F9 |F9 |Start recording macro
--- Shift+F9 |⇧F9 |F10 |Stop recording macro
--- Alt+F9 |⌥F9 |F12 |Play recorded macro
+-- F9 |F9 |F9 |Start/stop recording macro
+-- Shift+F9 |⇧F9 |F10 |Play recorded macro
-- Ctrl+U |⌘U |^U |Quickly open `_USERHOME`
-- None |None |None |Quickly open `_HOME`
-- Ctrl+Alt+Shift+O|^⌘⇧O |M-S-O |Quickly open current directory
-- Ctrl+Alt+Shift+P|^⌘⇧P |M-^P |Quickly open current project
+-- Ctrl+Shift+K |⌥⇧⇥ |M-S-K |Insert snippet...
+-- Tab |⇥ |Tab |Expand snippet or next placeholder
+-- Shift+Tab |⇧⇥ |S-Tab |Previous snippet placeholder
+-- Esc |Esc |Esc |Cancel snippet
+-- Ctrl+K |⌥⇥ |M-K |Complete trigger word
+-- Ctrl+Space |⌥Esc |^Space |Complete symbol
+-- Ctrl+H |^H |M-H<br/>M-S-H|Show documentation
-- Ctrl+I |⌘I |M-S-I |Show style
-- **Buffer** | | |
-- Ctrl+Tab |^⇥ |M-N |Next buffer
@@ -205,8 +205,8 @@ local M = {}
-- N/A |N/A |S-Tab |Focus replace buttons
-- Tab |⇥ |Down |Focus replace field
-- Shift+Tab |⇧⇥ |Up |Focus find field
--- Down |⇣ |^P |Cycle back through history
--- Up |⇡ |^N |Cycle forward through history
+-- Up |⇡ |^P |Cycle back through history
+-- Down |⇣ |^N |Cycle forward through history
-- N/A |N/A |F1 |Toggle "Match Case"
-- N/A |N/A |F2 |Toggle "Whole Word"
-- N/A |N/A |F3 |Toggle "Regex"
@@ -224,13 +224,11 @@ module('textadept.keys')]]
-- a: aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ_ ) ] } *+-/=\n\s
-- ca: aAbBcCdD F jJkKlLmM N qQ t xXy zZ_"'()[]{}<>* / \s
--
--- CTRL = 'c' (Control ^)
--- ALT = 'a' (Alt)
--- META = [unused]
--- SHIFT = 's' (Shift ⇧)
--- ADD = ''
--- Control, Alt, Shift, and 'a' = 'caA'
--- Control, Shift, and '\t' = 'cs\t'
+-- c = 'ctrl' (Control ^)
+-- a = 'alt' (Alt)
+-- s = 'shift' (Shift ⇧)
+-- Control, Alt, Shift, and 'a' = 'ctrl+alt+A'
+-- Control, Shift, and '\t' = 'ctrl+shift+\t'
--
-- Mac OSX key bindings.
--
@@ -239,21 +237,20 @@ module('textadept.keys')]]
-- c: cC D gG H J K L oO qQ xXyYzZ_ ) ] } * /
-- cm: aAbBcC~D F ~HiIjJkKlL~MnN p q~rRsStTuUvVwWxXyYzZ_"'()[]{}<>*+-/=\t\n
--
--- CTRL = 'c' (Control ^)
--- ALT = 'a' (Alt/option ⌥)
--- META = 'm' (Command ⌘)
--- SHIFT = 's' (Shift ⇧)
--- ADD = ''
--- Command, Option, Shift, and 'a' = 'amA'
--- Command, Shift, and '\t' = 'ms\t'
+-- c = 'ctrl' (Control ^)
+-- a = 'alt' (Alt/option ⌥)
+-- m = 'cmd' (Command ⌘)
+-- s = 'shift' (Shift ⇧)
+-- Command, Option, Shift, and 'a' = 'alt+cmd+A'
+-- Command, Shift, and '\t' = 'cmd+shift+\t'
--
-- Curses key bindings.
--
-- Key bindings available depend on your implementation of curses.
--
-- For ncurses (Linux, Mac OSX, BSD):
--- * The only Control keys recognized are 'ca'-'cz', 'c ', 'c\\', 'c]', 'c^',
--- and 'c_'.
+-- * The only Control keys recognized are 'ctrl+a'-'ctrl+z', 'ctrl+ ',
+-- 'ctrl+\\', 'ctrl+]', 'ctrl+^', and 'ctrl+_'.
-- * Control+Shift and Control+Meta+Shift keys are not recognized.
-- * Modifiers for function keys F1-F12 are not recognized.
-- For pdcurses (Win32):
@@ -267,305 +264,276 @@ module('textadept.keys')]]
-- m: e J qQ sS vVw yY _ +
-- Note: m[befhstv] may be used by Linux/BSD GUI terminals for menu access.
--
--- CTRL = 'c' (Control ^)
--- ALT = [unused]
--- META = 'm' (Alt)
--- SHIFT = 's' (Shift ⇧)
--- ADD = ''
--- Control, Meta, and 'a' = 'cma'
-
-local keys, OSX, GUI, CURSES, _L = keys, OSX, not CURSES, CURSES, _L
-
--- File.
-keys[not OSX and (GUI and 'cn' or 'cmn') or 'mn'] = buffer.new
-keys[not OSX and 'co' or 'mo'] = io.open_file
-keys[not OSX and GUI and 'cao' or 'cmo'] = io.open_recent_file
-keys[not OSX and (GUI and 'cO' or 'mo') or 'mO'] = buffer.reload
-keys[not OSX and 'cs' or 'ms'] = buffer.save
-keys[not OSX and (GUI and 'cS' or 'cms') or 'mS'] = buffer.save_as
--- TODO: io.save_all_files
-keys[not OSX and 'cw' or 'mw'] = buffer.close
-keys[not OSX and (GUI and 'cW' or 'cmw') or 'mW'] = io.close_all_buffers
--- TODO: textadept.sessions.load
--- TODO: textadept.sessions.save
-keys[not OSX and 'cq' or 'mq'] = quit
+-- c = 'ctrl' (Control ^)
+-- m = 'meta' (Alt)
+-- s = 'shift' (Shift ⇧)
+-- Control, Meta, and 'a' = 'ctrl+meta+a'
--- Edit.
+local _L = _L
local m_edit = textadept.menu.menubar[_L['Edit']]
-keys[not OSX and 'cz' or 'mz'] = buffer.undo
-if CURSES then keys.mz = keys.cz end -- ^Z suspends in some terminals
-if not OSX then keys.cy = buffer.redo end
-keys[not OSX and GUI and 'cZ' or 'mZ'] = buffer.redo
-keys[not OSX and 'cx' or 'mx'] = buffer.cut
-keys[not OSX and 'cc' or 'mc'] = buffer.copy
-keys[not OSX and 'cv' or 'mv'] = buffer.paste
-keys[not OSX and (GUI and 'cV' or 'mv') or 'mV'] =
- textadept.editing.paste_reindent
-if GUI then keys[not OSX and 'cd' or 'md'] = buffer.line_duplicate end
-keys.del = buffer.clear
-keys[not OSX and (GUI and 'adel' or 'mdel') or 'cdel'] =
- m_edit[_L['Delete Word']][2]
-keys[not OSX and GUI and 'ca' or 'ma'] = buffer.select_all
-keys[GUI and 'cm' or 'mm'] = m_edit[_L['Match Brace']][2]
-keys[not OSX and ((GUI or WIN32) and 'c\n' or 'cmj') or 'cesc'] =
- m_edit[_L['Complete Word']][2]
-if GUI then
- keys[not OSX and 'caH' or 'mH'] = textadept.editing.highlight_word
-end
-keys[not OSX and GUI and 'c/' or 'm/'] = textadept.editing.block_comment
-keys.ct = textadept.editing.transpose_chars
-keys[not OSX and (GUI and 'cJ' or 'mj') or 'cj'] = textadept.editing.join_lines
-keys[not OSX and (GUI and 'c|' or 'c\\') or 'm|'] =
- m_edit[_L['Filter Through']][2]
--- Select.
-local m_sel = m_edit[_L['Select']]
-keys[GUI and 'cM' or 'mM'] = m_sel[_L['Select between Matching Delimiters']][2]
-keys[not OSX and GUI and 'c<' or 'm<'] = m_sel[_L['Select between XML Tags']][2]
-if GUI then
- keys[not OSX and 'c>' or 'm>'] = m_sel[_L['Select in XML Tag']][2]
-end
-keys[not OSX and (GUI and 'cD' or 'mW') or 'mD'] = textadept.editing.select_word
-keys[not OSX and GUI and 'cN' or 'mN'] = textadept.editing.select_line
-keys[not OSX and GUI and 'cP' or 'mP'] = textadept.editing.select_paragraph
--- Selection.
-m_sel = m_edit[_L['Selection']]
-keys[not OSX and (GUI and 'cau' or 'cmu') or 'cu'] = buffer.upper_case
-keys[not OSX and (GUI and 'caU' or 'cml') or 'cU'] = buffer.lower_case
-keys[not OSX and (GUI and 'a<' or 'm>') or 'c<'] =
- m_sel[_L['Enclose as XML Tags']][2]
-if GUI then
- keys[not OSX and 'a>' or 'c>'] = m_sel[_L['Enclose as Single XML Tag']][2]
- keys[not OSX and "a'" or "c'"] = m_sel[_L['Enclose in Single Quotes']][2]
- keys[not OSX and 'a"' or 'c"'] = m_sel[_L['Enclose in Double Quotes']][2]
-end
-keys[not OSX and (GUI and 'a(' or 'm)') or 'c('] =
- m_sel[_L['Enclose in Parentheses']][2]
-keys[not OSX and (GUI and 'a[' or 'm]') or 'c['] =
- m_sel[_L['Enclose in Brackets']][2]
-keys[not OSX and (GUI and 'a{' or 'm}') or 'c{'] =
- m_sel[_L['Enclose in Braces']][2]
-keys.csup = buffer.move_selected_lines_up
-keys.csdown = buffer.move_selected_lines_down
-
--- Search.
+local m_sel, m_seln = m_edit[_L['Select']], m_edit[_L['Selection']]
local m_search = textadept.menu.menubar[_L['Search']]
-keys[not OSX and GUI and 'cf' or 'mf'] = m_search[_L['Find']][2]
-if CURSES then keys.mF = keys.mf end -- mf is used by some GUI terminals
-keys[not OSX and GUI and 'cg' or 'mg'] = ui.find.find_next
-if not OSX and GUI then keys.f3 = keys.cg end
-keys[not OSX and GUI and 'cG' or 'mG'] = ui.find.find_prev
-if not OSX and GUI then keys.sf3 = keys.cG end
-keys[not OSX and (GUI and 'car' or 'mr') or 'cr'] = ui.find.replace
-keys[not OSX and (GUI and 'caR' or 'mR') or 'cR'] = ui.find.replace_all
--- Find Next is an when find pane is focused in GUI.
--- Find Prev is ap when find pane is focused in GUI.
--- Replace is ar when find pane is focused in GUI.
--- Replace All is aa when find pane is focused in GUI.
-keys[not OSX and GUI and 'caf' or 'cmf'] = ui.find.find_incremental
-if GUI then
- keys[not OSX and 'cF' or 'mF'] = m_search[_L['Find in Files']][2]
-end
--- Find in Files is ai when find pane is focused in GUI.
-if GUI then
- keys[not OSX and 'cag' or 'cmg'] = m_search[_L['Goto Next File Found']][2]
- keys[not OSX and 'caG' or 'cmG'] = m_search[_L['Goto Previous File Found']][2]
-end
-keys[not OSX and 'cj' or 'mj'] = textadept.editing.goto_line
-
--- Tools.
local m_tools = textadept.menu.menubar[_L['Tools']]
-keys[not OSX and (GUI and 'ce' or 'mc') or 'me'] =
- m_tools[_L['Command Entry']][2]
-keys[not OSX and (GUI and 'cE' or 'mC') or 'mE'] =
- m_tools[_L['Select Command']][2]
-keys[not OSX and 'cr' or 'mr'] = textadept.run.run
-keys[not OSX and (GUI and 'cR' or 'cmr') or 'mR'] = textadept.run.compile
-keys[not OSX and (GUI and 'cB' or 'cmb') or 'mB'] = textadept.run.build
-if GUI then
- keys[not OSX and 'cA' or 'mA'] = m_tools[_L['Set Arguments...']][2]
-end
-keys[not OSX and (GUI and 'cX' or 'cmx') or 'mX'] = textadept.run.stop
-keys[not OSX and (GUI and 'cae' or 'mx') or 'cme'] =
- m_tools[_L['Next Error']][2]
-keys[not OSX and (GUI and 'caE' or 'mX') or 'cmE'] =
- m_tools[_L['Previous Error']][2]
--- Bookmark.
local m_bookmark = m_tools[_L['Bookmarks']]
-keys[not OSX and (GUI and 'cf2' or 'f1') or 'mf2'] = textadept.bookmarks.toggle
-keys[not OSX and (GUI and 'csf2' or 'f6') or 'msf2'] = textadept.bookmarks.clear
-keys.f2 = m_bookmark[_L['Next Bookmark']][2]
-keys[GUI and 'sf2' or 'f3'] = m_bookmark[_L['Previous Bookmark']][2]
-keys[GUI and 'af2' or 'f4'] = textadept.bookmarks.goto_mark
--- Macros.
-keys.f9 = textadept.macros.record
-keys[GUI and 'sf9' or 'f10'] = textadept.macros.play
--- Quick Open.
-local m_quick_open = m_tools[_L['Quick Open']]
-keys[not OSX and 'cu' or 'mu'] = m_quick_open[_L['Quickly Open User Home']][2]
--- TODO: m_quick_open[_L['Quickly Open Textadept Home']][2]
-keys[not OSX and (GUI and 'caO' or 'mO') or 'cmO'] =
- m_quick_open[_L['Quickly Open Current Directory']][2]
-keys[not OSX and (GUI and 'caP' or 'cmp') or 'cmP'] = io.quick_open
--- Snippets.
+local m_qopen = m_tools[_L['Quick Open']]
local m_snippets = m_tools[_L['Snippets']]
-keys[not OSX and (GUI and 'cK' or 'mK') or 'sa\t'] = textadept.snippets.select
-keys['\t'] = textadept.snippets.insert
-keys['s\t'] = textadept.snippets.previous
-keys.esc = textadept.snippets.cancel_current
-keys[not OSX and (GUI and 'ck' or 'mk') or 'a\t'] =
- m_snippets[_L['Complete Trigger Word']][2]
--- Other.
-keys[not OSX and 'c ' or 'aesc'] = m_tools[_L['Complete Symbol']][2]
-keys[GUI and 'ch' or 'mh'] = textadept.editing.show_documentation
-if CURSES then keys.mH = keys.mh end -- mh is used by some GUI terminals
-keys[not OSX and (GUI and 'ci' or 'mI') or 'mi'] = m_tools[_L['Show Style']][2]
-
--- Buffer.
local m_buffer = textadept.menu.menubar[_L['Buffer']]
-keys[GUI and 'c\t' or 'mn'] = m_buffer[_L['Next Buffer']][2]
-keys[GUI and 'cs\t' or 'mp'] = m_buffer[_L['Previous Buffer']][2]
-keys[not OSX and GUI and 'cb' or 'mb'] = ui.switch_buffer
-if CURSES then keys.mB = keys.mb end -- mb is used by some GUI terminals
--- Indentation.
local m_indentation = m_buffer[_L['Indentation']]
--- TODO: m_indentation[_L['Tab width: 2']][2]
--- TODO: m_indentation[_L['Tab width: 3']][2]
--- TODO: m_indentation[_L['Tab width: 4']][2]
--- TODO: m_indentation[_L['Tab width: 8']][2]
-keys[not OSX and (GUI and 'caT' or 'mt') or 'cT'] =
- m_indentation[_L['Toggle Use Tabs']][2]
-if CURSES then keys.mT = keys.mt end -- mt is used by some GUI terminals
-keys[not OSX and (GUI and 'cai' or 'mi') or 'ci'] =
- textadept.editing.convert_indentation
--- EOL Mode.
--- TODO: m_buffer[_L['EOL Mode']][_L['CRLF']][2]
--- TODO: m_buffer[_L['EOL Mode']][_L['LF']][2]
--- Encoding.
--- TODO: m_buffer[_L['Encoding']][_L['UTF-8 Encoding']][2]
--- TODO: m_buffer[_L['Encoding']][_L['ASCII Encoding']][2]
--- TODO: m_buffer[_L['Encoding']][_L['CP-1252 Encoding']][2]
--- TODO: m_buffer[_L['Encoding']][_L['UTF-16 Encoding']][2]
-if GUI then
- keys[not OSX and 'ca\n' or 'c\n'] = m_buffer[_L['Toggle View EOL']][2]
- keys[not OSX and 'ca\\' or 'c\\'] = m_buffer[_L['Toggle Wrap Mode']][2]
- keys[not OSX and 'caS' or 'cS'] = m_buffer[_L['Toggle View Whitespace']][2]
+local m_view = textadept.menu.menubar[_L['View']]
+local m_help = textadept.menu.menubar[_L['Help']]
+
+-- Bindings for Linux/Win32, Mac OSX, Terminal.
+local bindings = {
+ -- File.
+ [buffer.new] = {'ctrl+n', 'cmd+n', 'ctrl+meta+n'},
+ [io.open_file] = {'ctrl+o', 'cmd+o', 'ctrl+o'},
+ [io.open_recent_file] = {'ctrl+alt+o', 'ctrl+cmd+o', 'ctrl+alt+o'},
+ [buffer.reload] = {'ctrl+O', 'cmd+O', 'meta+o'},
+ [buffer.save] = {'ctrl+s', 'cmd+s', 'ctrl+s'},
+ [buffer.save_as] = {'ctrl+S', 'cmd+S', 'ctrl+meta+s'},
+ -- TODO: io.save_all_files
+ [buffer.close] = {'ctrl+w', 'cmd+w', 'ctrl+w'},
+ [io.close_all_buffers] = {'ctrl+W', 'cmd+W', 'ctrl+meta+w'},
+ -- TODO: textadept.sessions.load
+ -- TODO: textadept.sessions.save
+ [quit] = {'ctrl+q', 'cmd+q', 'ctrl+q'},
+
+ -- Edit.
+ [buffer.undo] = {'ctrl+z', 'cmd+z', {'ctrl+z', 'meta+z'}},
+ [buffer.redo] = {{'ctrl+y', 'ctrl+Z'}, 'cmd+Z', {'ctrl+y', 'meta+Z'}},
+ [buffer.cut] = {'ctrl+x', 'cmd+x', 'ctrl+x'},
+ [buffer.copy] = {'ctrl+c', 'cmd+c', 'ctrl+c'},
+ [buffer.paste] = {'ctrl+v', 'cmd+v', 'ctrl+v'},
+ [textadept.editing.paste_reindent] = {'ctrl+V', 'cmd+V', 'meta+v'},
+ [buffer.line_duplicate] = {'ctrl+d', 'cmd+d', nil},
+ [buffer.clear] = {'del', {'del', 'ctrl+d'}, {'del', 'ctrl+d'}},
+ [m_edit[_L['Delete Word']][2]] =
+ {'alt+del', 'ctrl+del', {'meta+del', 'meta+d'}},
+ [buffer.select_all] = {'ctrl+a', 'cmd+a', 'meta+a'},
+ [m_edit[_L['Match Brace']][2]] = {'ctrl+m', 'ctrl+m', 'meta+m'},
+ [m_edit[_L['Complete Word']][2]] =
+ {'ctrl+\n', 'ctrl+esc', {'ctrl+meta+j', 'ctrl+\n'}},
+ [textadept.editing.highlight_word] = {'ctrl+alt+H', 'cmd+H', nil},
+ [textadept.editing.block_comment] = {'ctrl+/', 'ctrl+/', 'meta+/'},
+ [textadept.editing.transpose_chars] = {'ctrl+t', 'ctrl+t', 'ctrl+t'},
+ [textadept.editing.join_lines] = {'ctrl+J', 'ctrl+j', 'meta+j'},
+ [m_edit[_L['Filter Through']][2]] = {'ctrl+|', 'cmd+|', 'ctrl+\\'},
+ -- Select.
+ [m_sel[_L['Select between Matching Delimiters']][2]] =
+ {'ctrl+M', 'ctrl+M', 'meta+M'},
+ [m_sel[_L['Select between XML Tags']][2]] = {'ctrl+<', 'cmd+<', 'meta+<'},
+ [m_sel[_L['Select in XML Tag']][2]] = {'ctrl+>', 'cmd+>', nil},
+ [textadept.editing.select_word] = {'ctrl+D', 'cmd+D', 'meta+W'},
+ [textadept.editing.select_line] = {'ctrl+N', 'cmd+N', 'meta+N'},
+ [textadept.editing.select_paragraph] = {'ctrl+P', 'cmd+P', 'meta+P'},
+ -- Selection.
+ [buffer.upper_case] = {'ctrl+alt+u', 'ctrl+u', 'ctrl+meta+u'},
+ [buffer.lower_case] = {'ctrl+alt+U', 'ctrl+U', 'ctrl+meta+l'},
+ [m_seln[_L['Enclose as XML Tags']][2]] = {'alt+<', 'ctrl+<', 'meta+>'},
+ [m_seln[_L['Enclose as Single XML Tag']][2]] = {'alt+>', 'ctrl+>', nil},
+ [m_seln[_L['Enclose in Single Quotes']][2]] = {"alt+'", "ctrl+'", nil},
+ [m_seln[_L['Enclose in Double Quotes']][2]] = {'alt+"', 'ctrl+"', nil},
+ [m_seln[_L['Enclose in Parentheses']][2]] = {'alt+(', 'ctrl+(', 'meta+)'},
+ [m_seln[_L['Enclose in Brackets']][2]] = {'alt+[', 'ctrl+[', 'meta+]'},
+ [m_seln[_L['Enclose in Braces']][2]] = {'alt+{', 'ctrl+{', 'meta+}'},
+ [buffer.move_selected_lines_up] =
+ {'ctrl+shift+up', 'ctrl+shift+up', 'ctrl+shift+up'},
+ [buffer.move_selected_lines_down] =
+ {'ctrl+shift+down', 'ctrl+shift+down', 'ctrl+shift+down'},
+
+ -- Search.
+ [m_search[_L['Find']][2]] = {'ctrl+f', 'cmd+f', {'meta+f', 'meta+F'}},
+ [ui.find.find_next] = {{'ctrl+g', 'f3'}, 'cmd+g', 'meta+g'},
+ [ui.find.find_prev] = {{'ctrl+G', 'shift+f3'}, 'cmd+G', 'meta+G'},
+ [ui.find.replace] = {'ctrl+alt+r', 'ctrl+r', 'meta+r'},
+ [ui.find.replace_all] = {'ctrl+alt+R', 'ctrl+R', 'meta+R'},
+ -- Find Next is an when find pane is focused in GUI.
+ -- Find Prev is ap when find pane is focused in GUI.
+ -- Replace is ar when find pane is focused in GUI.
+ -- Replace All is aa when find pane is focused in GUI.
+ [ui.find.find_incremental] = {'ctrl+alt+f', 'ctrl+cmd+f', 'ctrl+meta+f'},
+ [m_search[_L['Find in Files']][2]] = {'ctrl+F', 'cmd+F', nil},
+ -- Find in Files is ai when find pane is focused in GUI.
+ [m_search[_L['Goto Next File Found']][2]] = {'ctrl+alt+g', 'ctrl+cmd+g', nil},
+ [m_search[_L['Goto Previous File Found']][2]] =
+ {'ctrl+alt+G', 'ctrl+cmd+G', nil},
+ [textadept.editing.goto_line] = {'ctrl+j', 'cmd+j', 'ctrl+j'},
+
+ -- Tools.
+ [m_tools[_L['Command Entry']][2]] = {'ctrl+e', 'cmd+e', 'meta+c'},
+ [m_tools[_L['Select Command']][2]] = {'ctrl+E', 'cmd+E', 'meta+C'},
+ [textadept.run.run] = {'ctrl+r', 'cmd+r', 'ctrl+r'},
+ [textadept.run.compile] = {'ctrl+R', 'cmd+R', 'ctrl+meta+r'},
+ [m_tools[_L['Set Arguments...']][2]] = {'ctrl+A', 'cmd+A', nil},
+ [textadept.run.build] = {'ctrl+B', 'cmd+B', 'ctrl+meta+b'},
+ [textadept.run.stop] = {'ctrl+X', 'cmd+X', 'ctrl+meta+x'},
+ [m_tools[_L['Next Error']][2]] = {'ctrl+alt+e', 'ctrl+cmd+e', 'meta+x'},
+ [m_tools[_L['Previous Error']][2]] = {'ctrl+alt+E', 'ctrl+cmd+E', 'meta+X'},
+ -- Bookmark.
+ [textadept.bookmarks.toggle] = {'ctrl+f2', 'cmd+f2', 'f1'},
+ [textadept.bookmarks.clear] = {'ctrl+shift+f2', 'cmd+shift+f2', 'f6'},
+ [m_bookmark[_L['Next Bookmark']][2]] = {'f2', 'f2', 'f2'},
+ [m_bookmark[_L['Previous Bookmark']][2]] = {'shift+f2', 'shift+f2', 'f3'},
+ [textadept.bookmarks.goto_mark] = {'alt+f2', 'alt+f2', 'f4'},
+ -- Macros.
+ [textadept.macros.record] = {'f9', 'f9', 'f9'},
+ [textadept.macros.play] = {'shift+f9', 'shift+f9', 'f10'},
+ -- Quick Open.
+ [m_qopen[_L['Quickly Open User Home']][2]] = {'ctrl+u', 'cmd+u', 'ctrl+u'},
+ -- TODO: m_qopen[_L['Quickly Open Textadept Home']][2]
+ [m_qopen[_L['Quickly Open Current Directory']][2]] =
+ {'ctrl+alt+O', 'ctrl+cmd+O', 'meta+O'},
+ [io.quick_open] = {'ctrl+alt+P', 'ctrl+cmd+P', 'ctrl+meta+p'},
+ -- Snippets.
+ [textadept.snippets.select] = {'ctrl+K', 'shift+alt+\t', 'meta+K'},
+ [textadept.snippets.insert] = {'\t', '\t', '\t'},
+ [textadept.snippets.previous] = {'shift+\t', 'shift+\t', 'shift+\t'},
+ [textadept.snippets.cancel_current] = {'esc', 'esc', 'esc'},
+ [m_snippets[_L['Complete Trigger Word']][2]] = {'ctrl+k', 'alt+\t', 'meta+k'},
+ -- Other.
+ [m_tools[_L['Complete Symbol']][2]] = {'ctrl+ ', 'alt+esc', 'ctrl+ '},
+ [textadept.editing.show_documentation] =
+ {'ctrl+h', 'ctrl+h', {'meta+h', 'meta+H'}},
+ [m_tools[_L['Show Style']][2]] = {'ctrl+i', 'cmd+i', 'meta+I'},
+
+ -- Buffer.
+ [m_buffer[_L['Next Buffer']][2]] = {'ctrl+\t', 'ctrl+\t', 'meta+n'},
+ [m_buffer[_L['Previous Buffer']][2]] =
+ {'ctrl+shift+\t', 'ctrl+shift+\t', 'meta+p'},
+ [ui.switch_buffer] = {'ctrl+b', 'cmd+b', {'meta+b', 'meta+B'}},
+ -- Indentation.
+ -- TODO: m_indentation[_L['Tab width: 2']][2]
+ -- TODO: m_indentation[_L['Tab width: 3']][2]
+ -- TODO: m_indentation[_L['Tab width: 4']][2]
+ -- TODO: m_indentation[_L['Tab width: 8']][2]
+ [m_indentation[_L['Toggle Use Tabs']][2]] =
+ {'ctrl+alt+T', 'ctrl+T', {'meta+t', 'meta+T'}},
+ [textadept.editing.convert_indentation] = {'ctrl+alt+i', 'ctrl+i', 'meta+i'},
+ -- EOL Mode.
+ -- TODO: m_buffer[_L['EOL Mode']][_L['CRLF']][2]
+ -- TODO: m_buffer[_L['EOL Mode']][_L['LF']][2]
+ -- Encoding.
+ -- TODO: m_buffer[_L['Encoding']][_L['UTF-8 Encoding']][2]
+ -- TODO: m_buffer[_L['Encoding']][_L['ASCII Encoding']][2]
+ -- TODO: m_buffer[_L['Encoding']][_L['CP-1252 Encoding']][2]
+ -- TODO: m_buffer[_L['Encoding']][_L['UTF-16 Encoding']][2]
+ [m_buffer[_L['Toggle View EOL']][2]] = {'ctrl+alt+\n', 'ctrl+\n', nil},
+ [m_buffer[_L['Toggle Wrap Mode']][2]] = {'ctrl+alt+\\', 'ctrl+\\', nil},
+ [m_buffer[_L['Toggle View Whitespace']][2]] = {'ctrl+alt+S', 'ctrl+S', nil},
+ [textadept.file_types.select_lexer] = {'ctrl+L', 'cmd+L', 'meta+L'},
+ [m_buffer[_L['Refresh Syntax Highlighting']][2]] =
+ {'f5', 'f5', {'f5', 'ctrl+l'}},
+
+ -- View.
+ [m_view[_L['Next View']][2]] = {'ctrl+alt+n', 'ctrl+alt+\t', nil},
+ [m_view[_L['Previous View']][2]] = {'ctrl+alt+p', 'ctrl+alt+shift+\t', nil},
+ [m_view[_L['Split View Horizontal']][2]] =
+ {{'ctrl+alt+s', 'ctrl+alt+h'}, 'ctrl+s', nil},
+ [m_view[_L['Split View Vertical']][2]] = {'ctrl+alt+v', 'ctrl+v', nil},
+ [m_view[_L['Unsplit View']][2]] = {'ctrl+alt+w', 'ctrl+w', nil},
+ [m_view[_L['Unsplit All Views']][2]] = {'ctrl+alt+W', 'ctrl+W', nil},
+ [m_view[_L['Grow View']][2]] =
+ {{'ctrl+alt++', 'ctrl+alt+='}, {'ctrl++', 'ctrl+='}, nil},
+ [m_view[_L['Shrink View']][2]] = {'ctrl+alt+-', 'ctrl+-', nil},
+ [m_view[_L['Toggle Current Fold']][2]] = {'ctrl+*', 'cmd+*', 'meta+*'},
+ [m_view[_L['Toggle Show Indent Guides']][2]] = {'ctrl+alt+I', 'ctrl+I', nil},
+ [m_view[_L['Toggle Virtual Space']][2]] = {'ctrl+alt+V', 'ctrl+V', nil},
+ [view.zoom_in] = {'ctrl+=', 'cmd+=', nil},
+ [view.zoom_out] = {'ctrl+-', 'cmd+-', nil},
+ [m_view[_L['Reset Zoom']][2]] = {'ctrl+0', 'cmd+0', nil},
+
+ -- Help.
+ [m_help[_L['Show Manual']][2]] = {'f1', 'f1', nil},
+ [m_help[_L['Show LuaDoc']][2]] = {'shift+f1', 'shift+f1', nil},
+
+ -- Movement commands.
+ -- Unbound keys are handled by Scintilla, but when playing back a macro, this
+ -- is not possible. Define some useful default key bindings so Scintilla does
+ -- not have to handle them. Note that Scintilla still will handle some keys.
+ [buffer.line_down] = {'down', {'down', 'ctrl+n'}, {'down', 'ctrl+n'}},
+ [buffer.line_down_extend] =
+ {'shift+down', {'shift+down', 'ctrl+N'}, 'shift+down'},
+ [buffer.line_up] = {'up', {'up', 'ctrl+p'}, {'up', 'ctrl+p'}},
+ [buffer.line_up_extend] = {'shift+up', {'shift+up', 'ctrl+P'}, 'shift+up'},
+ [buffer.char_left] = {'left', {'left', 'ctrl+b'}, {'left', 'ctrl+b'}},
+ [buffer.char_left_extend] =
+ {'shift+left', {'shift+left', 'ctrl+B'}, 'shift+left'},
+ [buffer.word_left] = {'ctrl+left', {'ctrl+left', 'ctrl+cmd+b'}, 'ctrl+left'},
+ [buffer.word_left_extend] =
+ {'ctrl+shift+left', {'ctrl+shift+left', 'ctrl+cmd+B'}, 'ctrl+shift+left'},
+ [buffer.char_right] = {'right', {'right', 'ctrl+f'}, {'right', 'ctrl+f'}},
+ [buffer.char_right_extend] =
+ {'shift+right', {'shift+right', 'ctrl+F'}, 'shift+right'},
+ [buffer.word_right] =
+ {'ctrl+right', {'ctrl+right', 'ctrl+cmd+f'}, 'ctrl+right'},
+ [buffer.word_right_end_extend] = {
+ 'ctrl+shift+right', {'ctrl+shift+right', 'ctrl+cmd+F'}, 'ctrl+shift+right'
+ },
+ [buffer.vc_home] = {'home', {'cmd+left', 'ctrl+a'}, {'home', 'ctrl+a'}},
+ [buffer.vc_home_extend] =
+ {'shift+home', {'cmd+shift+left', 'ctrl+A'}, 'meta+A'},
+ [buffer.line_end] = {'end', {'cmd+right', 'ctrl+e'}, {'end', 'ctrl+e'}},
+ [buffer.line_end_extend] =
+ {'shift+end', {'cmd+shift+right', 'ctrl+E'}, 'meta+E'},
+ [view.vertical_center_caret] = {nil, 'ctrl+l', nil},
+ [buffer.page_up_extend] = {nil, nil, 'meta+U'},
+ [buffer.page_down_extend] = {nil, nil, 'meta+D'},
+ [buffer.document_start] = {nil, nil, 'ctrl+meta+a'},
+ [buffer.document_end] = {nil, nil, 'ctrl+meta+e'},
+
+ [function(b)
+ buffer:line_end_extend()
+ if not buffer.selection_empty then buffer:cut() else buffer:clear() end
+ end] = {nil, 'ctrl+k', 'ctrl+k'},
+ [buffer.del_word_right] = {'ctrl+del', 'cmd+del', 'ctrl+del'},
+ [buffer.del_line_right] =
+ {'ctrl+shift+del', 'cmd+shift+del', 'ctrl+shift+del'},
+ [buffer.delete_back] = {'\b', '\b', {'\b', 'ctrl+h'}},
+ [buffer.del_word_left] = {'ctrl+\b', 'cmd+\b', nil},
+ [buffer.del_line_left] = {'ctrl+shift+\b', 'cmd+shift+\b', nil},
+ [function() buffer.selection_mode = 0 end] = {nil, nil, 'ctrl+^'},
+ [buffer.swap_main_anchor_caret] = {nil, nil, 'ctrl+]'},
+
+ -- Other.
+ -- UTF-8 input.
+ [function()
+ ui.command_entry.run(
+ function(code) buffer:add_text(utf8.char(tonumber(code, 16))) end)
+ end] = {nil, 'cmd+U', 'meta+u'}
+}
+
+local keys, plat = keys, CURSES and 3 or OSX and 2 or 1
+for f, plat_keys in pairs(bindings) do
+ local key = plat_keys[plat]
+ if type(key) == 'string' then
+ keys[key] = f
+ elseif type(key) == 'table' then
+ for _, key in ipairs(key) do keys[key] = f end
+ end
end
-keys[not OSX and GUI and 'cL' or 'mL'] = textadept.file_types.select_lexer
-keys.f5 = m_buffer[_L['Refresh Syntax Highlighting']][2]
-if CURSES then keys.cl = keys.f5 end
--- View.
-local m_view = textadept.menu.menubar[_L['View']]
-if GUI then
- keys[not OSX and 'can' or 'ca\t'] = m_view[_L['Next View']][2]
- keys[not OSX and 'cap' or 'cas\t'] = m_view[_L['Previous View']][2]
- keys[not OSX and 'cas' or 'cs'] = m_view[_L['Split View Horizontal']][2]
- if not OSX then keys.cah = keys.cas end
- keys[not OSX and 'cav' or 'cv'] = m_view[_L['Split View Vertical']][2]
- keys[not OSX and 'caw' or 'cw'] = m_view[_L['Unsplit View']][2]
- keys[not OSX and 'caW' or 'cW'] = m_view[_L['Unsplit All Views']][2]
- keys[not OSX and 'ca+' or 'c+'] = m_view[_L['Grow View']][2]
- keys[not OSX and 'ca=' or 'c='] = keys[not OSX and 'ca+' or 'c+']
- keys[not OSX and 'ca-' or 'c-'] = m_view[_L['Shrink View']][2]
-else
- keys.cmv = {
+if CURSES then
+ keys['ctrl+meta+v'] = {
n = m_view[_L['Next View']][2],
p = m_view[_L['Previous View']][2],
s = m_view[_L['Split View Horizontal']][2],
+ h = m_view[_L['Split View Horizontal']][2],
v = m_view[_L['Split View Vertical']][2],
w = m_view[_L['Unsplit View']][2],
W = m_view[_L['Unsplit All Views']][2],
['+'] = m_view[_L['Grow View']][2],
+ ['='] = m_view[_L['Grow View']][2],
['-'] = m_view[_L['Shrink View']][2]
}
- if not OSX then keys.cmv.h = keys.cmv.s end
- keys.cmv['='] = keys.cmv['+']
-end
-keys[not OSX and GUI and 'c*' or 'm*'] = m_view[_L['Toggle Current Fold']][2]
-if GUI then
- keys[not OSX and 'caI' or 'cI'] = m_view[_L['Toggle Show Indent Guides']][2]
- keys[not OSX and 'caV' or 'cV'] = m_view[_L['Toggle Virtual Space']][2]
end
-keys[not OSX and GUI and 'c=' or 'm='] = view.zoom_in
-keys[not OSX and GUI and 'c-' or 'm-'] = view.zoom_out
-keys[not OSX and GUI and 'c0' or 'm0'] = m_view[_L['Reset Zoom']][2]
--- Help.
-if GUI then
- keys.f1 = textadept.menu.menubar[_L['Help']][_L['Show Manual']][2]
- keys.sf1 = textadept.menu.menubar[_L['Help']][_L['Show LuaDoc']][2]
-end
+-- GTK-OSX reports Fn-key as a single keycode which confuses Scintilla. Do
+-- not propagate it.
+if OSX then keys.fn = function() return true end end
--- Movement commands.
-if OSX then
- keys.cf, keys.cF = buffer.char_right, buffer.char_right_extend
- keys.cmf, keys.cmF = buffer.word_right, buffer.word_right_extend
- keys.cb, keys.cB = buffer.char_left, buffer.char_left_extend
- keys.cmb, keys.cmB = buffer.word_left, buffer.word_left_extend
- keys.cn, keys.cN = buffer.line_down, buffer.line_down_extend
- keys.cp, keys.cP = buffer.line_up, buffer.line_up_extend
- keys.ca, keys.cA = buffer.vc_home, buffer.vc_home_extend
- keys.ce, keys.cE = buffer.line_end, buffer.line_end_extend
- keys.aright, keys.aleft = buffer.word_right, buffer.word_left
- keys.cd = buffer.clear
- keys.ck = function()
- buffer:line_end_extend()
- if not buffer.selection_empty then buffer:cut() else buffer:clear() end
- end
- keys.cl = view.vertical_center_caret
- -- GTK-OSX reports Fn-key as a single keycode which confuses Scintilla. Do
- -- not propagate it.
- keys.fn = function() return true end
-elseif CURSES then
- keys['c^'] = function() buffer.selection_mode = 0 end
- keys['c]'] = buffer.swap_main_anchor_caret
- keys.cf, keys.cb = buffer.char_right, buffer.char_left
- keys.cn, keys.cp = buffer.line_down, buffer.line_up
- keys.ca, keys.ce = buffer.vc_home, buffer.line_end
- keys.mA, keys.mE = buffer.vc_home_extend, buffer.line_end_extend
- keys.mU, keys.mD = buffer.page_up_extend, buffer.page_down_extend
- keys.cma, keys.cme = buffer.document_start, buffer.document_end
- keys.cd, keys.md, keys.ch = buffer.clear, keys.mdel, buffer.delete_back
- keys.ck = function()
- buffer:line_end_extend()
- if not buffer.selection_empty then buffer:cut() else buffer:clear() end
- end
-end
-
--- Unbound keys are handled by Scintilla, but when playing back a macro, this is
--- not possible. Define useful default key bindings so Scintilla does not have
--- to handle them.
-keys.left, keys.sleft = buffer.char_left, buffer.char_left_extend
-keys.cleft, keys.csleft = buffer.word_left, buffer.word_left_extend
-keys.right, keys.sright = buffer.char_right, buffer.char_right_extend
-keys.cright, keys.csright = buffer.word_right, buffer.word_right_extend
-if OSX then
- keys.mleft, keys.msleft = buffer.vc_home, buffer.vc_home_extend
- keys.mright, keys.msright = buffer.line_end, buffer.line_end_extend
-end
-keys.down, keys.sdown = buffer.line_down, buffer.line_down_extend
-keys.up, keys.sup = buffer.line_up, buffer.line_up_extend
-if not OSX then
- keys.home, keys.shome = buffer.vc_home, buffer.vc_home_extend
- keys['end'], keys.send = buffer.line_end, buffer.line_end_extend
-end
-keys.del, keys.sdel = buffer.clear, buffer.cut
-keys[not OSX and 'cdel' or 'mdel'] = buffer.del_word_right
-keys[not OSX and 'csdel' or 'msdel'] = buffer.del_line_right
-keys['\b'], keys['s\b'] = buffer.delete_back, buffer.delete_back
-keys[not OSX and 'c\b' or 'm\b'] = buffer.del_word_left
-keys[not OSX and 'cs\b' or 'ms\b'] = buffer.del_line_left
-
--- Other.
-ui.find.find_incremental_keys.cr = function()
- ui.find.find_incremental(ui.command_entry:get_text(), false, true) -- reverse
-end
-if OSX or CURSES then
- -- UTF-8 input.
- keys[OSX and 'mU' or 'mu'] = function()
- ui.command_entry.run(
- function(code) buffer:add_text(utf8.char(tonumber(code, 16))) end)
- end
+-- Reverse incremental find.
+ui.find.find_incremental_keys['ctrl+r'] = function()
+ ui.find.find_incremental(ui.command_entry:get_text(), false, true)
end
return M
diff --git a/modules/textadept/menu.lua b/modules/textadept/menu.lua
index 5db2d259..4702ecbc 100644
--- a/modules/textadept/menu.lua
+++ b/modules/textadept/menu.lua
@@ -15,14 +15,15 @@ module('textadept.menu')]]
local _L = _L
local SEPARATOR = {''}
--- The following buffer functions need to be made constant in order for menu
--- items to identify the key associated with the functions.
+-- The following buffer and view functions need to be made constant in order for
+-- menu items to identify the key associated with the functions.
local menu_buffer_functions = {
'undo', 'redo', 'cut', 'copy', 'paste', 'line_duplicate', 'clear',
'select_all', 'upper_case', 'lower_case', 'move_selected_lines_up',
- 'move_selected_lines_down', 'zoom_in', 'zoom_out'
+ 'move_selected_lines_down'
}
for _, f in ipairs(menu_buffer_functions) do buffer[f] = buffer[f] end
+view.zoom_in, view.zoom_out = view.zoom_in, view.zoom_out
-- Commonly used functions in menu commands.
local sel_enc = textadept.editing.select_enclosed
@@ -398,11 +399,11 @@ local key_shortcuts, menu_items, contextmenu_items
-- @return keycode and modifier mask
local function get_gdk_key(key_seq)
if not key_seq then return nil end
- local mods, key = key_seq:match('^([cams]*)(.+)$')
- if not mods or not key then return nil end
- local modifiers = ((mods:find('s') or key:lower() ~= key) and 1 or 0) +
- (mods:find('c') and 4 or 0) + (mods:find('a') and 8 or 0) +
- (mods:find('m') and 0x10000000 or 0)
+ local mods, key = key_seq:match('^(.*%+)(.+)$')
+ if not mods and not key then mods, key = '', key_seq end
+ local modifiers = ((mods:find('shift%+') or key:lower() ~= key) and 1 or 0) +
+ (mods:find('ctrl%+') and 4 or 0) + (mods:find('alt%+') and 8 or 0) +
+ (mods:find('cmd%+') and 0x10000000 or 0)
local code = string.byte(key)
if #key > 1 or code < 32 then
for i, s in pairs(keys.KEYSYMS) do
diff --git a/test/test.lua b/test/test.lua
index 4121c8ca..08881604 100644
--- a/test/test.lua
+++ b/test/test.lua
@@ -554,14 +554,14 @@ function test_file_io_quick_open_interactive()
end
function test_keys_keychain()
- local ca = keys.ca
+ local ctrl_a = keys['ctrl+a']
local foo = false
- keys.ca = {a = function() foo = true end}
+ keys['ctrl+a'] = {a = function() foo = true end}
events.emit(events.KEYPRESS, string.byte('a'))
assert(not foo, 'foo set outside keychain')
events.emit(events.KEYPRESS, string.byte('a'), false, true)
assert_equal(#keys.keychain, 1)
- --assert_equal(keys.keychain[1], 'ca')
+ assert_equal(keys.keychain[1], 'ctrl+a')
events.emit(events.KEYPRESS, not CURSES and 0xFF1B or 7) -- esc
assert_equal(#keys.keychain, 0, 'keychain not canceled')
events.emit(events.KEYPRESS, string.byte('a'))
@@ -569,7 +569,7 @@ function test_keys_keychain()
events.emit(events.KEYPRESS, string.byte('a'), false, true)
events.emit(events.KEYPRESS, string.byte('a'))
assert(foo, 'foo not set')
- keys.ca = ca -- restore
+ keys['ctrl+a'] = ctrl_a -- restore
end
function test_keys_propagation()