-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathinit.lua
215 lines (199 loc) · 6.84 KB
/
init.lua
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
208
209
210
211
212
213
214
215
-- Copyright 2014-2025 Mitchell. See LICENSE.
--- A Textadept module for loading an interactive Lua REPL using the editor's Lua State, similar
-- to Lua's interactive REPL.
--
-- This is an alternative to the single-line Lua command entry.
--
-- Install this module by copying it into your *~/.textadept/modules/* directory or Textadept's
-- *modules/* directory, and then putting the following in your *~/.textadept/init.lua*:
--
-- require('lua_repl')
--
-- Select "Tools > Lua REPL" to open the REPL. Typing the Enter key on any line evaluates that
-- line, unless that line is a continuation line. In that case, when finished, select the lines
-- to evaluate and type Enter to evaluate the entire chunk.
--
-- Lines may be optionally prefixed with '=' (similar to the Lua prompt) to print a result.
-- @module lua_repl
local M = {}
-- Localizations.
if not rawget(_L, 'Lua REPL') then _L['Lua REPL'] = 'L_ua REPL' end
--- A special environment for a Lua REPL.
-- It has an `__index` metafield for accessing Textadept's global environment.
-- @table env
-- @local
local env = setmetatable({
print = function(...)
buffer:add_text('--> ')
local args = table.pack(...)
for i = 1, args.n do
buffer:add_text(tostring(args[i]))
if i < args.n then buffer:add_text('\t') end
end
buffer:new_line()
end
}, {__index = _G})
--- Lua command history.
-- It has a numeric `pos` field that indicates where in the history the user currently is.
M.history = {pos = 0}
--- Evaluates as Lua code the current line or the text on the currently selected lines.
-- If the current line has a syntax error, it is ignored and treated as a line continuation.
function M.evaluate_repl()
local s, e = buffer.selection_start, buffer.selection_end
local code, last_line
if s ~= e then -- use selected lines as code
local i, j = buffer:line_from_position(s), buffer:line_from_position(e)
if i < j then
s = buffer:position_from_line(i)
if buffer.column[e] > 1 then e = buffer:position_from_line(j + 1) end
end
code = buffer:text_range(s, e)
last_line = buffer:line_from_position(e)
else -- use line as input
code = buffer:get_cur_line()
last_line = buffer:line_from_position(buffer.current_pos)
end
local f, result = load('return ' .. code, 'repl', 't', env)
if not f then f, result = load(code, 'repl', 't', env) end
if not f and s == e then return false end -- multi-line chunk; propagate key
buffer:goto_pos(buffer.line_end_position[last_line])
buffer:new_line()
if f then result = select(2, pcall(f)) end
if result then
buffer:add_text('--> ')
if type(result) == 'table' then
-- Pretty-print tables like ui.command_entry does.
local items = {}
for k, v in pairs(result) do items[#items + 1] = string.format('%s = %s', k, v) end
table.sort(items)
result = string.format('{%s}', table.concat(items, ', '))
if view.edge_column > 0 and #result > view.edge_column then
local indent = string.rep(' ', buffer.tab_width)
result = string.format('{\n%s%s\n}', indent, table.concat(items, ',\n' .. indent))
end
end
buffer:add_text(tostring(result):gsub('(\r?\n)', '%1--> '))
buffer:new_line()
end
M.history[#M.history + 1] = code
M.history.pos = #M.history + 1
buffer:set_save_point()
end
--- Shows a set of Lua code completions for the current position.
function M.complete_lua()
local line, pos = buffer:get_cur_line()
local symbol, op, part = line:sub(1, pos - 1):match('([%w_.]-)([%.:]?)([%w_]*)$')
local ok, result = pcall((load(string.format('return (%s)', symbol), nil, 't', env)))
if (not ok or type(result) ~= 'table') and symbol ~= '' then return end
local cmpls = {}
part = '^' .. part
if not ok or symbol == 'buffer' then
local sci = _SCINTILLA
local global_envs = not ok and {_G} or
(op == ':' and {sci.functions} or {sci.properties, sci.constants})
for i = 1, #global_envs do
for k in pairs(global_envs[i]) do
if type(k) == 'string' and k:find(part) then cmpls[#cmpls + 1] = k end
end
end
else
for k, v in pairs(result) do
if type(k) == 'string' and k:find(part) and (op == '.' or type(v) == 'function') then
cmpls[#cmpls + 1] = k
end
end
end
table.sort(cmpls)
buffer.auto_c_separator, buffer.auto_c_order = string.byte(' '), buffer.ORDER_PRESORTED
buffer:auto_c_show(#part - 1, table.concat(cmpls, ' '))
end
--- Cycle backward through command history, taking into account commands with multiple lines.
function M.cycle_history_prev()
if buffer:auto_c_active() then
buffer:line_up()
return
end
if M.history.pos <= 1 then return end
for _ in (M.history[M.history.pos] or ''):gmatch('\n') do
buffer:line_delete()
buffer:delete_back()
end
buffer:line_delete()
M.history.pos = math.max(M.history.pos - 1, 1)
buffer:add_text(M.history[M.history.pos])
end
--- Cycle forward through command history, taking into account commands with multiple lines.
function M.cycle_history_next()
if buffer:auto_c_active() then
buffer:line_down()
return
end
if M.history.pos >= #M.history then return end
for _ in (M.history[M.history.pos] or ''):gmatch('\n') do
buffer:line_delete()
buffer:delete_back()
end
buffer:line_delete()
M.history.pos = math.min(M.history.pos + 1, #M.history)
buffer:add_text(M.history[M.history.pos])
end
--- Table of key bindings for the REPL.
-- @field keys
-- This separation is needed to prevent LDoc from parsing the following table.
M.keys = {
['\n'] = M.evaluate_repl, --
['ctrl+ '] = M.complete_lua, --
['ctrl+up'] = M.cycle_history_prev, --
['ctrl+down'] = M.cycle_history_next, --
['ctrl+p'] = M.cycle_history_prev, --
['ctrl+n'] = M.cycle_history_next
}
--- Register REPL keys.
local function register_keys()
if not keys.lua[next(M.keys)] then
for key, f in pairs(M.keys) do
keys.lua[key] = function()
if buffer._type ~= '[Lua REPL]' then return false end -- propagate
f()
end
end
end
end
events.connect(events.RESET_AFTER, register_keys)
--- Creates or switches to a Lua REPL.
-- If *new* is `true`, creates a new REPL even if one already exists.
-- @param new Flag that indicates whether or not to create a new REPL even if one already exists.
function M.open(new)
local repl_view, repl_buf = nil, nil
for i = 1, #_VIEWS do
if _VIEWS[i].buffer._type == '[Lua REPL]' then
repl_view = _VIEWS[i]
break
end
end
for i = 1, #_BUFFERS do
if _BUFFERS[i]._type == '[Lua REPL]' then
repl_buf = _BUFFERS[i]
break
end
end
if new or not (repl_view or repl_buf) then
buffer.new()._type = '[Lua REPL]'
buffer:set_lexer('lua')
buffer:add_text('-- ' .. _L['Lua REPL']:gsub('[_&]', ''))
buffer:new_line()
buffer:set_save_point()
register_keys()
else
if repl_view then
ui.goto_view(repl_view)
else
view:goto_buffer(repl_buf)
end
buffer:document_end() -- in case it's been scrolled in the meantime
end
end
-- Add REPL to Tools menu.
table.insert(textadept.menu.menubar['Tools'], {''})
table.insert(textadept.menu.menubar['Tools'], {_L['Lua REPL'], M.open})
return M