1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
-- 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.
local 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):lower()
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
|