1-- Copyright 2007-2021 Mitchell. See LICENSE.
2
3local M = {}
4
5--[[ This comment is for LuaDoc.
6---
7-- Defines key bindings for Textadept.
8-- This set of key bindings is pretty standard among other text editors, at
9-- least for basic editing commands and movements.
10--
11-- ### Key Bindings
12--
13-- Win32, Linux, BSD|macOS|Terminal|Command
14-- -----------------|-----|--------|--------
15-- **File**         |     |        |
16-- Ctrl+N           |⌘N   |M-^N    |New file
17-- Ctrl+O           |⌘O   |^O      |Open file
18-- Ctrl+Alt+O       |^⌘O  |M-^O    |Open recent file...
19-- Ctrl+Shift+O     |⌘⇧O  |M-O     |Reload file
20-- Ctrl+S           |⌘S   |^S      |Save file
21-- Ctrl+Shift+S     |⌘⇧S  |M-^S    |Save file as..
22-- None             |None |None    |Save all files
23-- Ctrl+W           |⌘W   |^W      |Close file
24-- Ctrl+Shift+W     |⌘⇧W  |M-^W    |Close all files
25-- None             |None |None    |Load session...
26-- None             |None |None    |Save session...
27-- Ctrl+Q           |⌘Q   |^Q      |Quit
28-- **Edit**               |         |              |
29-- Ctrl+Z<br/>Alt+Bksp    |⌘Z       |^Z^(†)<br/>M-Z|Undo
30-- Ctrl+Y<br/>Ctrl+Shift+Z|⌘⇧Z      |^Y<br/>M-S-Z  |Redo
31-- Ctrl+X<br/>Shift+Del   |⌘X<br/>⇧⌦|^X            |Cut
32-- Ctrl+C<br/>Ctrl+Ins    |⌘C       |^C            |Copy
33-- Ctrl+V<br/>Shift+Ins   |⌘V       |^V            |Paste
34-- Ctrl+Shift+V           |⌘⇧V      |M-V           |Paste Reindent
35-- Ctrl+D                 |⌘D       |None          |Duplicate line
36-- Del                    |⌦<br/>^D |Del<br/>^D    |Delete
37-- Alt+Del                |^⌦       |M-Del<br/>M-D |Delete word
38-- Ctrl+A                 |⌘A       |M-A           |Select all
39-- Ctrl+M                 |^M       |M-M           |Match brace
40-- Ctrl+Enter             |^Esc     |M-Enter^(‡)   |Complete word
41-- Ctrl+/                 |^/       |M-/           |Toggle block comment
42-- Ctrl+T                 |^T       |^T            |Transpose characters
43-- Ctrl+Shift+J           |^J       |M-J           |Join lines
44-- Ctrl+&#124;            |⌘&#124;  |^\            |Filter text through
45-- Ctrl+Shift+M           |^⇧M      |M-S-M         |Select between delimiters
46-- Ctrl+<                 |⌘<       |M-<           |Select between XML tags
47-- Ctrl+>                 |⌘>       |None          |Select in XML tag
48-- Ctrl+Shift+D           |⌘⇧D      |M-S-W         |Select word
49-- Ctrl+Shift+N           |⌘⇧N      |M-S-N         |Select line
50-- Ctrl+Shift+P           |⌘⇧P      |M-S-P         |Select paragraph
51-- Ctrl+Alt+U             |^U       |M-^U          |Upper case selection
52-- Ctrl+Alt+Shift+U       |^⇧U      |M-^L          |Lower case selection
53-- Alt+<                  |^<       |M->           |Enclose as XML tags
54-- Alt+>                  |^>       |None          |Enclose as single XML tag
55-- Alt+"                  |^"       |None          |Enclose in double quotes
56-- Alt+'                  |^'       |None          |Enclose in single quotes
57-- Alt+(                  |^(       |M-)           |Enclose in parentheses
58-- Alt+[                  |^[       |M-]           |Enclose in brackets
59-- Alt+{                  |^{       |M-}           |Enclose in braces
60-- Ctrl+Shift+Up          |^⇧⇡      |S-^Up         |Move selected lines up
61-- Ctrl+Shift+Down        |^⇧⇣      |S-^Down       |Move selected lines down
62-- Alt+,                  |^,       |M-,           |Navigate backward
63-- Alt+.                  |^.       |M-.           |Navigate forward
64-- None                   |None     |None          |Record location
65-- None                   |None     |None          |Clear navigation history
66-- Ctrl+P                 |⌘,       |M-~           |Preferences
67-- **Search**               |    |             |
68-- Ctrl+F                   |⌘F  |M-F<br/>M-S-F|Find
69-- Ctrl+G<br/>F3            |⌘G  |M-G          |Find next
70-- Ctrl+Shift+G<br/>Shift+F3|⌘⇧G |M-S-G        |Find previous
71-- Ctrl+Alt+R               |^R  |M-R          |Replace
72-- Ctrl+Alt+Shift+R         |^⇧R |M-S-R        |Replace all
73-- Ctrl+Alt+F               |^⌘F |M-^F         |Find incremental
74-- Ctrl+Shift+F             |⌘⇧F |None         |Find in files
75-- Ctrl+Alt+G               |^⌘G |None         |Goto next file found
76-- Ctrl+Alt+Shift+G         |^⌘⇧G|None         |Goto previous file found
77-- Ctrl+J                   |⌘J  |^J           |Jump to line
78-- **Tools**       |       |             |
79-- Ctrl+E          |⌘E     |M-C          |Command entry
80-- Ctrl+Shift+E    |⌘⇧E    |M-S-C        |Select command
81-- Ctrl+R          |⌘R     |^R           |Run
82-- Ctrl+Shift+R    |⌘⇧R    |M-^R         |Compile
83-- Ctrl+Shift+A    |⌘⇧A    |None         |Set Arguments...
84-- Ctrl+Shift+B    |⌘⇧B    |M-^B         |Build
85-- Ctrl+Shift+T    |⌘⇧T    |M-^T         |Run tests
86-- Ctrl+Shift+X    |⌘⇧X    |M-^X         |Stop
87-- Ctrl+Alt+E      |^⌘E    |M-X          |Next Error
88-- Ctrl+Alt+Shift+E|^⌘⇧E   |M-S-X        |Previous Error
89-- Ctrl+F2         |⌘F2    |F1           |Toggle bookmark
90-- Ctrl+Shift+F2   |⌘⇧F2   |F6           |Clear bookmarks
91-- F2              |F2     |F2           |Next bookmark
92-- Shift+F2        |⇧F2    |F3           |Previous bookmark
93-- Alt+F2          |⌥F2    |F4           |Goto bookmark...
94-- F9              |F9     |F9           |Start/stop recording macro
95-- Shift+F9        |⇧F9    |F10          |Play recorded macro
96-- Ctrl+U          |⌘U     |^U           |Quickly open `_USERHOME`
97-- None            |None   |None         |Quickly open `_HOME`
98-- Ctrl+Alt+Shift+O|^⌘⇧O   |M-S-O        |Quickly open current directory
99-- Ctrl+Alt+Shift+P|^⌘⇧P   |M-^P         |Quickly open current project
100-- Ctrl+Shift+K    |⌥⇧⇥    |M-S-K        |Insert snippet...
101-- Tab             |⇥      |Tab          |Expand snippet or next placeholder
102-- Shift+Tab       |⇧⇥     |S-Tab        |Previous snippet placeholder
103-- Esc             |Esc    |Esc          |Cancel snippet
104-- Ctrl+K          |⌥⇥     |M-K          |Complete trigger word
105-- Ctrl+Space      |⌥Esc   |^Space       |Complete symbol
106-- Ctrl+H          |^H     |M-H<br/>M-S-H|Show documentation
107-- Ctrl+I          |⌘I     |M-S-I        |Show style
108-- **Buffer**      |      |             |
109-- Ctrl+Tab        |^⇥    |M-N          |Next buffer
110-- Ctrl+Shift+Tab  |^⇧⇥   |M-P          |Previous buffer
111-- Ctrl+B          |⌘B    |M-B<br/>M-S-B|Switch to buffer...
112-- None            |None  |None         |Tab width: 2
113-- None            |None  |None         |Tab width: 3
114-- None            |None  |None         |Tab width: 4
115-- None            |None  |None         |Tab width: 8
116-- Ctrl+Alt+Shift+T|^⇧T   |M-T<br/>M-S-T|Toggle use tabs
117-- Ctrl+Alt+I      |^I    |M-I          |Convert indentation
118-- None            |None  |None         |CR+LF EOL mode
119-- None            |None  |None         |LF EOL mode
120-- None            |None  |None         |UTF-8 encoding
121-- None            |None  |None         |ASCII encoding
122-- None            |None  |None         |CP-1252 encoding
123-- None            |None  |None         |UTF-16 encoding
124-- Ctrl+Alt+\\     |^\\   |None         |Toggle wrap mode
125-- Ctrl+Alt+Shift+S|^⇧S   |None         |Toggle view whitespace
126-- Ctrl+Shift+L    |⌘⇧L   |M-S-L        |Select lexer...
127-- **View**                 |         |                 |
128-- Ctrl+Alt+N               |^⌥⇥      |M-^V N           |Next view
129-- Ctrl+Alt+P               |^⌥⇧⇥     |M-^V P           |Previous view
130-- Ctrl+Alt+S<br/>Ctrl+Alt+H|^S       |M-^V S<br/>M-^V H|Split view horizontal
131-- Ctrl+Alt+V               |^V       |M-^V V           |Split view vertical
132-- Ctrl+Alt+W               |^W       |M-^V W           |Unsplit view
133-- Ctrl+Alt+Shift+W         |^⇧W      |M-^V S-W         |Unsplit all views
134-- Ctrl+Alt++<br/>Ctrl+Alt+=|^+<br/>^=|M-^V +<br/>M-^V =|Grow view
135-- Ctrl+Alt+-               |^-       |M-^V -           |Shrink view
136-- Ctrl+*                   |⌘*       |M-*              |Toggle current fold
137-- Ctrl+Alt+Shift+I         |^⇧I      |N/A              |Toggle indent guides
138-- Ctrl+Alt+Shift+V         |^⇧V      |None             |Toggle virtual space
139-- Ctrl+=                   |⌘=       |N/A              |Zoom in
140-- Ctrl+-                   |⌘-       |N/A              |Zoom out
141-- Ctrl+0                   |⌘0       |N/A              |Reset zoom
142-- **Help**|    |    |
143-- F1      |F1  |None|Open manual
144-- Shift+F1|⇧F1 |None|Open LuaDoc
145-- None    |None|None|About
146-- **Movement**    |            |            |
147-- Down            |⇣<br/>^N    |^N<br/>Down |Line down
148-- Shift+Down      |⇧⇣<br/>^⇧N  |S-Down      |Line down extend selection
149-- Ctrl+Down       |^⇣          |^Down       |Scroll line down
150-- Alt+Shift+Down  |⌥⇧⇣         |M-S-Down    |Line down extend rect. selection
151-- Up              |⇡<br/>^P    |^P<br/>Up   |Line up
152-- Shift+Up        |⇧⇡<br/>^⇧P  |S-Up        |Line up extend selection
153-- Ctrl+Up         |^⇡          |^Up         |Scroll line up
154-- Alt+Shift+Up    |⌥⇧⇡         |M-S-Up      |Line up extend rect. selection
155-- Left            |⇠<br/>^B    |^B<br/>Left |Char left
156-- Shift+Left      |⇧⇠<br/>^⇧B  |S-Left      |Char left extend selection
157-- Ctrl+Left       |⌥⇠<br/>^⌘B  |^Left       |Word left
158-- Ctrl+Shift+Left |^⇧⇠<br/>^⌘⇧B|S-^Left     |Word left extend selection
159-- Alt+Shift+Left  |⌥⇧⇠         |M-S-Left    |Char left extend rect. selection
160-- Right           |⇢<br/>^F    |^F<br/>Right|Char right
161-- Shift+Right     |⇧⇢<br/>^⇧F  |S-Right     |Char right extend selection
162-- Ctrl+Right      |⌥⇢<br/>^⌘F  |^Right      |Word right
163-- Ctrl+Shift+Right|^⇧⇢<br/>^⌘⇧F|S-^Right    |Word right extend selection
164-- Alt+Shift+Right |⌥⇧⇢         |M-S-Right   |Char right extend rect. selection
165-- Home            |⌘⇠<br/>^A   |^A<br/>Home |Line start
166-- Shift+Home      |⌘⇧⇠<br/>^⇧A |M-S-A       |Line start extend selection
167-- Ctrl+Home       |⌘⇡<br/>⌘↖   |M-^A        |Document start
168-- Ctrl+Shift+Home |⌘⇧⇡<br/>⌘⇧↖ |None        |Document start extend selection
169-- Alt+Shift+Home  |⌥⇧↖         |None        |Line start extend rect. selection
170-- End             |⌘⇢<br/>^E   |^E<br/>End  |Line end
171-- Shift+End       |⌘⇧⇢<br/>^⇧E |M-S-E       |Line end extend selection
172-- Ctrl+End        |⌘⇣<br/>⌘↘   |M-^E        |Document end
173-- Ctrl+Shift+End  |⌘⇧⇣<br/>⌘⇧↘ |None        |Document end extend selection
174-- Alt+Shift+End   |⌥⇧↘         |None        |Line end extend rect. selection
175-- PgUp            |⇞           |PgUp        |Page up
176-- Shift+PgUp      |⇧⇞          |M-S-U       |Page up extend selection
177-- Alt+Shift+PgUp  |⌥⇧⇞         |None        |Page up extend rect. selection
178-- PgDn            |⇟           |PgDn        |Page down
179-- Shift+PgDn      |⇧⇟          |M-S-D       |Page down extend selection
180-- Alt+Shift+PgDn  |⌥⇧⇟         |None        |Page down extend rect. selection
181-- Ctrl+Del        |⌘⌦          |^Del        |Delete word right
182-- Ctrl+Shift+Del  |⌘⇧⌦         |S-^Del      |Delete line right
183-- Ins             |Ins         |Ins         |Toggle overtype
184-- Bksp            |⌫<br/>⇧⌫    |^H<br/>Bksp |Delete back
185-- Ctrl+Bksp       |⌘⌫          |None        |Delete word left
186-- Ctrl+Shift+Bksp |⌘⇧⌫         |None        |Delete line left
187-- Tab             |⇥           |Tab<br/>^I  |Insert tab or indent
188-- Shift+Tab       |⇧⇥          |S-Tab       |Dedent
189-- None            |^K          |^K          |Cut to line end
190-- None            |^L          |None        |Center line vertically
191-- N/A             |N/A         |^^          |Mark text at the caret position
192-- N/A             |N/A         |^]          |Swap caret and mark anchor
193-- **UTF-8 Input**          |            |                |
194-- Ctrl+Shift+U *xxxx* Enter|⌘⇧U *xxxx* ↩|M-U *xxxx* Enter|Insert U-*xxxx* char.
195-- **Find Fields**|               |            |
196-- Left           |⇠<br/>^B       |^B<br/>Left |Cursor left
197-- Right          |⇢<br/>^F       |^F<br/>Right|Cursor right
198-- Del            |⌦              |Del         |Delete forward
199-- Bksp           |⌫              |^H<br/>Bksp |Delete back
200-- Ctrl+V         |⌘V             |^V          |Paste
201-- N/A            |N/A            |^X          |Cut all
202-- N/A            |N/A            |^Y          |Copy all
203-- N/A            |N/A            |^U          |Erase all
204-- Home           |↖<br/>⌘⇠<br/>^A|^A          |Home
205-- End            |↘<br/>⌘⇢<br/>^E|^E          |End
206-- N/A            |N/A            |^T          |Transpose characters
207-- N/A            |N/A            |Tab         |Toggle find/replace buttons
208-- Tab            |⇥              |Down        |Focus replace field
209-- Shift+Tab      |⇧⇥             |Up          |Focus find field
210-- Up             |⇡              |^P          |Cycle back through history
211-- Down           |⇣              |^N          |Cycle forward through history
212-- N/A            |N/A            |F1          |Toggle "Match Case"
213-- N/A            |N/A            |F2          |Toggle "Whole Word"
214-- N/A            |N/A            |F3          |Toggle "Regex"
215-- N/A            |N/A            |F4          |Toggle "Find in Files"
216--
217-- †: Some terminals interpret ^Z as suspend; see FAQ for workaround.
218--
219-- ‡: Ctrl+Enter in Windows terminal version.
220module('textadept.keys')]]
221
222-- Windows, Linux, and BSD key bindings.
223--
224-- Unassigned keys (~ denotes keys reserved by the operating system):
225-- c:       C         H I               Q       ~ V     Y  _   ) ] }   +
226-- a:  aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ_   ) ] }  *+-/=\n\s
227-- ca: aAbBcCdD   F   H  jJkKlLmM N    qQ    t       xXy zZ_"'()[]{}<>*  / \n\s
228--
229-- c = 'ctrl' (Control ^)
230-- a = 'alt' (Alt)
231-- s = 'shift' (Shift ⇧)
232-- Control, Alt, Shift, and 'a' = 'ctrl+alt+A'
233-- Control, Shift, and '\t' = 'ctrl+shift+\t'
234--
235-- macOS key bindings.
236--
237-- Unassigned keys (~ denotes keys reserved by the operating system):
238-- m:       C        ~H I JkK  ~M    p  ~    t    V    yY  _   ) ] }   +   ~~\n
239-- c:      cC D    gG H   J K L    oO  qQ            xXyYzZ_   ) ] }  *  /   \n
240-- cm: aAbBcC~D   F  ~HiIjJkKlL~MnN  p q~rRsStTuUvVwWxXyYzZ_"'()[]{}<>*+-/=\t\n
241--
242-- c = 'ctrl' (Control ^)
243-- a = 'alt' (Alt/option ⌥)
244-- m = 'cmd' (Command ⌘)
245-- s = 'shift' (Shift ⇧)
246-- Command, Option, Shift, and 'a' = 'alt+cmd+A'
247-- Command, Shift, and '\t' = 'cmd+shift+\t'
248--
249-- Curses key bindings.
250--
251-- Key bindings available depend on your implementation of curses.
252--
253-- For ncurses (Linux, macOS, BSD):
254--   * The only Control keys recognized are 'ctrl+a'-'ctrl+z', 'ctrl+ ',
255--     'ctrl+\\', 'ctrl+]', 'ctrl+^', and 'ctrl+_'.
256--   * Control+Shift and Control+Meta+Shift keys are not recognized.
257--   * Modifiers for function keys F1-F12 are not recognized.
258-- For pdcurses (Win32):
259--   * Many Control+Symbol keys are not recognized, but most
260--     Control+Shift+Symbol keys are.
261--   * Ctrl+Meta+Symbol keys are not recognized.
262--
263-- Unassigned keys (~ denotes keys reserved by the operating system):
264-- c:        g~~  l~            ~
265-- cm:   cd  g~~ k ~   q       yz
266-- m:          e          J            qQ  sS    vVw   yY  _          +
267-- Note: m[befhstv] may be used by Linux/BSD GUI terminals for menu access.
268--
269-- c = 'ctrl' (Control ^)
270-- m = 'meta' (Alt)
271-- s = 'shift' (Shift ⇧)
272-- Control, Meta, and 'a' = 'ctrl+meta+a'
273
274local _L = _L
275-- Returns the menu command associated with the '/'-separated string of menu
276-- labels.
277-- Labels are automatically localized.
278-- @param labels Path to the menu command.
279-- @usage m('Edit/Select/Select in XML Tag')
280local function m(labels)
281  local menu = textadept.menu.menubar
282  for label in labels:gmatch('[^/]+') do menu = menu[_L[label]] end
283  return menu[2]
284end
285
286-- Bindings for Linux/Win32, macOS, Terminal.
287local bindings = {
288  -- File.
289  [buffer.new] = {'ctrl+n', 'cmd+n', 'ctrl+meta+n'},
290  [io.open_file] = {'ctrl+o', 'cmd+o', 'ctrl+o'},
291  [io.open_recent_file] = {'ctrl+alt+o', 'ctrl+cmd+o', 'ctrl+alt+o'},
292  [buffer.reload] = {'ctrl+O', 'cmd+O', 'meta+o'},
293  [buffer.save] = {'ctrl+s', 'cmd+s', 'ctrl+s'},
294  [buffer.save_as] = {'ctrl+S', 'cmd+S', 'ctrl+meta+s'},
295  -- TODO: io.save_all_files
296  [buffer.close] = {'ctrl+w', 'cmd+w', 'ctrl+w'},
297  [io.close_all_buffers] = {'ctrl+W', 'cmd+W', 'ctrl+meta+w'},
298  -- TODO: textadept.sessions.load
299  -- TODO: textadept.sessions.save
300  [quit] = {'ctrl+q', 'cmd+q', 'ctrl+q'},
301
302  -- Edit.
303  [buffer.undo] = {'ctrl+z', 'cmd+z', {'ctrl+z', 'meta+z'}},
304  [buffer.redo] = {{'ctrl+y', 'ctrl+Z'}, 'cmd+Z', {'ctrl+y', 'meta+Z'}},
305  [buffer.cut] = {'ctrl+x', 'cmd+x', 'ctrl+x'},
306  [buffer.copy] = {'ctrl+c', 'cmd+c', 'ctrl+c'},
307  [buffer.paste] = {'ctrl+v', 'cmd+v', 'ctrl+v'},
308  [textadept.editing.paste_reindent] = {'ctrl+V', 'cmd+V', 'meta+v'},
309  [buffer.line_duplicate] = {'ctrl+d', 'cmd+d', nil},
310  [buffer.clear] = {'del', {'del', 'ctrl+d'}, {'del', 'ctrl+d'}},
311  [m('Edit/Delete Word')] = {'alt+del', 'ctrl+del', {'meta+del', 'meta+d'}},
312  [buffer.select_all] = {'ctrl+a', 'cmd+a', 'meta+a'},
313  [m('Edit/Match Brace')] = {'ctrl+m', 'ctrl+m', 'meta+m'},
314  [m('Edit/Complete Word')] =
315    {'ctrl+\n', 'ctrl+esc', {'ctrl+meta+j', 'ctrl+\n'}},
316  [textadept.editing.toggle_comment] = {'ctrl+/', 'ctrl+/', 'meta+/'},
317  [textadept.editing.transpose_chars] = {'ctrl+t', 'ctrl+t', 'ctrl+t'},
318  [textadept.editing.join_lines] = {'ctrl+J', 'ctrl+j', 'meta+j'},
319  [m('Edit/Filter Through')] = {'ctrl+|', 'cmd+|', 'ctrl+\\'},
320  -- Select.
321  [m('Edit/Select/Select between Matching Delimiters')] =
322    {'ctrl+M', 'ctrl+M', 'meta+M'},
323  [m('Edit/Select/Select between XML Tags')] = {'ctrl+<', 'cmd+<', 'meta+<'},
324  [m('Edit/Select/Select in XML Tag')] = {'ctrl+>', 'cmd+>', nil},
325  [textadept.editing.select_word] = {'ctrl+D', 'cmd+D', 'meta+W'},
326  [textadept.editing.select_line] = {'ctrl+N', 'cmd+N', 'meta+N'},
327  [textadept.editing.select_paragraph] = {'ctrl+P', 'cmd+P', 'meta+P'},
328  -- Selection.
329  [buffer.upper_case] = {'ctrl+alt+u', 'ctrl+u', 'ctrl+meta+u'},
330  [buffer.lower_case] = {'ctrl+alt+U', 'ctrl+U', 'ctrl+meta+l'},
331  [m('Edit/Selection/Enclose as XML Tags')] = {'alt+<', 'ctrl+<', 'meta+>'},
332  [m('Edit/Selection/Enclose as Single XML Tag')] = {'alt+>', 'ctrl+>', nil},
333  [m('Edit/Selection/Enclose in Single Quotes')] = {"alt+'", "ctrl+'", nil},
334  [m('Edit/Selection/Enclose in Double Quotes')] = {'alt+"', 'ctrl+"', nil},
335  [m('Edit/Selection/Enclose in Parentheses')] = {'alt+(', 'ctrl+(', 'meta+)'},
336  [m('Edit/Selection/Enclose in Brackets')] = {'alt+[', 'ctrl+[', 'meta+]'},
337  [m('Edit/Selection/Enclose in Braces')] = {'alt+{', 'ctrl+{', 'meta+}'},
338  [buffer.move_selected_lines_up] =
339    {'ctrl+shift+up', 'ctrl+shift+up', 'ctrl+shift+up'},
340  [buffer.move_selected_lines_down] =
341    {'ctrl+shift+down', 'ctrl+shift+down', 'ctrl+shift+down'},
342  -- History.
343  [textadept.history.back] = {'alt+,', 'ctrl+,', 'meta+,'},
344  [textadept.history.forward] = {'alt+.', 'ctrl+.', 'meta+.'},
345  -- TODO: textadept.history.record
346  -- TODO: textadept.history.clear
347  -- Preferences.
348  [m('Edit/Preferences')] = {'ctrl+p', 'cmd+,', 'meta+~'},
349
350  -- Search.
351  [ui.find.focus] = {'ctrl+f', 'cmd+f', {'meta+f', 'meta+F'}},
352  [ui.find.find_next] = {{'ctrl+g', 'f3'}, 'cmd+g', 'meta+g'},
353  [ui.find.find_prev] = {{'ctrl+G', 'shift+f3'}, 'cmd+G', 'meta+G'},
354  [ui.find.replace] = {'ctrl+alt+r', 'ctrl+r', 'meta+r'},
355  [ui.find.replace_all] = {'ctrl+alt+R', 'ctrl+R', 'meta+R'},
356  -- Find Next is an when find pane is focused in GUI.
357  -- Find Prev is ap when find pane is focused in GUI.
358  -- Replace is ar when find pane is focused in GUI.
359  -- Replace All is aa when find pane is focused in GUI.
360  [m('Search/Find Incremental')] = {'ctrl+alt+f', 'ctrl+cmd+f', 'ctrl+meta+f'},
361  [m('Search/Find in Files')] = {'ctrl+F', 'cmd+F', nil},
362  -- Find in Files is ai when find pane is focused in GUI.
363  [m('Search/Goto Next File Found')] = {'ctrl+alt+g', 'ctrl+cmd+g', nil},
364  [m('Search/Goto Previous File Found')] = {'ctrl+alt+G', 'ctrl+cmd+G', nil},
365  [textadept.editing.goto_line] = {'ctrl+j', 'cmd+j', 'ctrl+j'},
366
367  -- Tools.
368  [ui.command_entry.run] = {'ctrl+e', 'cmd+e', 'meta+c'},
369  [m('Tools/Select Command')] = {'ctrl+E', 'cmd+E', 'meta+C'},
370  [textadept.run.run] = {'ctrl+r', 'cmd+r', 'ctrl+r'},
371  [textadept.run.compile] = {'ctrl+R', 'cmd+R', 'ctrl+meta+r'},
372  [textadept.run.set_arguments] = {'ctrl+A', 'cmd+A', nil},
373  [textadept.run.build] = {'ctrl+B', 'cmd+B', 'ctrl+meta+b'},
374  [textadept.run.test] = {'ctrl+T', 'cmd+T', 'ctrl+meta+t'},
375  [textadept.run.stop] = {'ctrl+X', 'cmd+X', 'ctrl+meta+x'},
376  [m('Tools/Next Error')] = {'ctrl+alt+e', 'ctrl+cmd+e', 'meta+x'},
377  [m('Tools/Previous Error')] = {'ctrl+alt+E', 'ctrl+cmd+E', 'meta+X'},
378  -- Bookmark.
379  [textadept.bookmarks.toggle] = {'ctrl+f2', 'cmd+f2', 'f1'},
380  [textadept.bookmarks.clear] = {'ctrl+shift+f2', 'cmd+shift+f2', 'f6'},
381  [m('Tools/Bookmarks/Next Bookmark')] = {'f2', 'f2', 'f2'},
382  [m('Tools/Bookmarks/Previous Bookmark')] = {'shift+f2', 'shift+f2', 'f3'},
383  [textadept.bookmarks.goto_mark] = {'alt+f2', 'alt+f2', 'f4'},
384  -- Macros.
385  [textadept.macros.record] = {'f9', 'f9', 'f9'},
386  [textadept.macros.play] = {'shift+f9', 'shift+f9', 'f10'},
387  -- Quick Open.
388  [m('Tools/Quick Open/Quickly Open User Home')] =
389    {'ctrl+u', 'cmd+u', 'ctrl+u'},
390  -- TODO: m('Tools/Quickly Open Textadept Home')
391  [m('Tools/Quick Open/Quickly Open Current Directory')] =
392    {'ctrl+alt+O', 'ctrl+cmd+O', 'meta+O'},
393  [io.quick_open] = {'ctrl+alt+P', 'ctrl+cmd+P', 'ctrl+meta+p'},
394  -- Snippets.
395  [textadept.snippets.select] = {'ctrl+K', 'shift+alt+\t', 'meta+K'},
396  [textadept.snippets.insert] = {'\t', '\t', '\t'},
397  [textadept.snippets.previous] = {'shift+\t', 'shift+\t', 'shift+\t'},
398  [textadept.snippets.cancel_current] = {'esc', 'esc', 'esc'},
399  [m('Tools/Snippets/Complete Trigger Word')] = {'ctrl+k', 'alt+\t', 'meta+k'},
400  -- Other.
401  [m('Tools/Complete Symbol')] = {'ctrl+ ', 'alt+esc', 'ctrl+ '},
402  [textadept.editing.show_documentation] =
403    {'ctrl+h', 'ctrl+h', {'meta+h', 'meta+H'}},
404  [m('Tools/Show Style')] = {'ctrl+i', 'cmd+i', 'meta+I'},
405
406  -- Buffer.
407  [m('Buffer/Next Buffer')] = {'ctrl+\t', 'ctrl+\t', 'meta+n'},
408  [m('Buffer/Previous Buffer')] = {'ctrl+shift+\t', 'ctrl+shift+\t', 'meta+p'},
409  [ui.switch_buffer] = {'ctrl+b', 'cmd+b', {'meta+b', 'meta+B'}},
410  -- Indentation.
411  -- TODO: m('Buffer/Indentation/Tab width: 2')
412  -- TODO: m('Buffer/Indentation/Tab width: 3')
413  -- TODO: m('Buffer/Indentation/Tab width: 4')
414  -- TODO: m('Buffer/Indentation/Tab width: 8')
415  [m('Buffer/Indentation/Toggle Use Tabs')] =
416    {'ctrl+alt+T', 'ctrl+T', {'meta+t', 'meta+T'}},
417  [textadept.editing.convert_indentation] = {'ctrl+alt+i', 'ctrl+i', 'meta+i'},
418  -- EOL Mode.
419  -- TODO: m('Buffer/EOL Mode/CRLF')
420  -- TODO: m('Buffer/EOL Mode/LF')
421  -- Encoding.
422  -- TODO: m('Buffer/Encoding/UTF-8 Encoding')
423  -- TODO: m('Buffer/Encoding/ASCII Encoding')
424  -- TODO: m('Buffer/Encoding/CP-1252 Encoding')
425  -- TODO: m('Buffer/Encoding/UTF-16 Encoding')
426  [m('Buffer/Toggle Wrap Mode')] = {'ctrl+alt+\\', 'ctrl+\\', nil},
427  [m('Buffer/Toggle View Whitespace')] = {'ctrl+alt+S', 'ctrl+S', nil},
428  [textadept.file_types.select_lexer] = {'ctrl+L', 'cmd+L', 'meta+L'},
429
430  -- View.
431  [m('View/Next View')] = {'ctrl+alt+n', 'ctrl+alt+\t', nil},
432  [m('View/Previous View')] = {'ctrl+alt+p', 'ctrl+alt+shift+\t', nil},
433  [m('View/Split View Horizontal')] =
434    {{'ctrl+alt+s', 'ctrl+alt+h'}, 'ctrl+s', nil},
435  [m('View/Split View Vertical')] = {'ctrl+alt+v', 'ctrl+v', nil},
436  [m('View/Unsplit View')] = {'ctrl+alt+w', 'ctrl+w', nil},
437  [m('View/Unsplit All Views')] = {'ctrl+alt+W', 'ctrl+W', nil},
438  [m('View/Grow View')] =
439    {{'ctrl+alt++', 'ctrl+alt+='}, {'ctrl++', 'ctrl+='}, nil},
440  [m('View/Shrink View')] = {'ctrl+alt+-', 'ctrl+-', nil},
441  [m('View/Toggle Current Fold')] = {'ctrl+*', 'cmd+*', 'meta+*'},
442  [m('View/Toggle Show Indent Guides')] = {'ctrl+alt+I', 'ctrl+I', nil},
443  [m('View/Toggle Virtual Space')] = {'ctrl+alt+V', 'ctrl+V', nil},
444  [view.zoom_in] = {'ctrl+=', 'cmd+=', nil},
445  [view.zoom_out] = {'ctrl+-', 'cmd+-', nil},
446  [m('View/Reset Zoom')] = {'ctrl+0', 'cmd+0', nil},
447
448  -- Help.
449  [m('Help/Show Manual')] = {'f1', 'f1', nil},
450  [m('Help/Show LuaDoc')] = {'shift+f1', 'shift+f1', nil},
451
452  -- Movement commands.
453  -- Unbound keys are handled by Scintilla, but when playing back a macro, this
454  -- is not possible. Define some useful default key bindings so Scintilla does
455  -- not have to handle them. Note that Scintilla still will handle some keys.
456  [buffer.line_down] = {'down', {'down', 'ctrl+n'}, {'down', 'ctrl+n'}},
457  [buffer.line_down_extend] =
458    {'shift+down', {'shift+down', 'ctrl+N'}, 'shift+down'},
459  [buffer.line_up] = {'up', {'up', 'ctrl+p'}, {'up', 'ctrl+p'}},
460  [buffer.line_up_extend] = {'shift+up', {'shift+up', 'ctrl+P'}, 'shift+up'},
461  [buffer.char_left] = {'left', {'left', 'ctrl+b'}, {'left', 'ctrl+b'}},
462  [buffer.char_left_extend] =
463    {'shift+left', {'shift+left', 'ctrl+B'}, 'shift+left'},
464  [buffer.word_left] = {'ctrl+left', {'alt+left', 'ctrl+cmd+b'}, 'ctrl+left'},
465  [buffer.word_left_extend] =
466    {'ctrl+shift+left', {'ctrl+shift+left', 'ctrl+cmd+B'}, 'ctrl+shift+left'},
467  [buffer.char_right] = {'right', {'right', 'ctrl+f'}, {'right', 'ctrl+f'}},
468  [buffer.char_right_extend] =
469    {'shift+right', {'shift+right', 'ctrl+F'}, 'shift+right'},
470  [buffer.word_right] =
471    {'ctrl+right', {'alt+right', 'ctrl+cmd+f'}, 'ctrl+right'},
472  [buffer.word_right_end_extend] = {
473    'ctrl+shift+right', {'ctrl+shift+right', 'ctrl+cmd+F'}, 'ctrl+shift+right'
474  },
475  [buffer.vc_home] = {'home', {'cmd+left', 'ctrl+a'}, {'home', 'ctrl+a'}},
476  [buffer.vc_home_extend] =
477    {'shift+home', {'cmd+shift+left', 'ctrl+A'}, 'meta+A'},
478  [buffer.line_end] = {'end', {'cmd+right', 'ctrl+e'}, {'end', 'ctrl+e'}},
479  [buffer.line_end_extend] =
480    {'shift+end', {'cmd+shift+right', 'ctrl+E'}, 'meta+E'},
481  [view.vertical_center_caret] = {nil, 'ctrl+l', nil},
482  [buffer.page_up_extend] = {nil, nil, 'meta+U'},
483  [buffer.page_down_extend] = {nil, nil, 'meta+D'},
484  [buffer.document_start] = {nil, nil, 'ctrl+meta+a'},
485  [buffer.document_end] = {nil, nil, 'ctrl+meta+e'},
486
487  [function()
488    buffer:line_end_extend()
489    if not buffer.selection_empty then buffer:cut() else buffer:clear() end
490  end] = {nil, 'ctrl+k', 'ctrl+k'},
491  [buffer.del_word_right] = {'ctrl+del', 'cmd+del', 'ctrl+del'},
492  [buffer.del_line_right] =
493    {'ctrl+shift+del', 'cmd+shift+del', 'ctrl+shift+del'},
494  [buffer.delete_back] = {'\b', '\b', {'\b', 'ctrl+h'}},
495  [buffer.del_word_left] = {'ctrl+\b', 'cmd+\b', nil},
496  [buffer.del_line_left] = {'ctrl+shift+\b', 'cmd+shift+\b', nil},
497  [function() buffer.selection_mode = 0 end] = {nil, nil, 'ctrl+^'},
498  [buffer.swap_main_anchor_caret] = {nil, nil, 'ctrl+]'},
499
500  -- Other.
501  -- UTF-8 input.
502  [function()
503    ui.command_entry.run(
504      function(code) buffer:add_text(utf8.char(tonumber(code, 16))) end)
505  end] = {nil, 'cmd+U', 'meta+u'}
506}
507
508local keys, plat = keys, CURSES and 3 or OSX and 2 or 1
509for f, plat_keys in pairs(bindings) do
510  local key = plat_keys[plat]
511  if type(key) == 'string' then
512    keys[key] = f
513  elseif type(key) == 'table' then
514    for _, key in ipairs(key) do keys[key] = f end
515  end
516end
517
518if CURSES then
519  keys['ctrl+meta+v'] = {
520    n = m('View/Next View'), p = m('View/Previous View'),
521    s = m('View/Split View Horizontal'), h = m('View/Split View Horizontal'),
522    v = m('View/Split View Vertical'), w = m('View/Unsplit View'),
523    W = m('View/Unsplit All Views'), ['+'] = m('View/Grow View'),
524    ['='] = m('View/Grow View'), ['-'] = m('View/Shrink View')
525  }
526end
527
528-- GTK-OSX reports Fn-key as a single keycode which confuses Scintilla. Do
529-- not propagate it.
530if OSX then keys.fn = function() return true end end
531
532return M
533