1 -- SPDX-License-Identifier: GPL-3.0-or-later
2 -- © 2020 Georgi Kirilov
5 auto_square_brackets
= false,
11 local textadept
= require
"textadept"
12 local events
= require
"events"
13 local keys
= require
"keys"
18 -- copied from core/keys.lua
19 local CTRL
, ALT
--[[, CMD]], SHIFT
= 'ctrl+', not CURSES
and 'alt+' or 'meta+' --[[, 'cmd+']], 'shift+'
21 -- XXX: in Lua 5.2 unpack() was moved into table
22 local unpack
= table.unpack
or unpack
27 local init
, supported
= unpack((require(cwd
..".parkour")))
30 local initialized
= false
33 setmetatable(M
.lispwords
, {__index
= function(t
, filetype
)
34 if supported
[filetype
] then
35 local recfg
= {__newindex
= function(_
, k
, v
)
36 events
.connect(events
.FILE_OPENED
, function()
37 if filetype
== buffer
.lexer_language
then
38 env
[buffer
].fmt
.lispwords
[k
] = v
42 t
[filetype
] = setmetatable({}, recfg
)
47 local H
= {M
= {}, T
= {}, O
= {}, I
= {}, A
= {}} -- handler functions
48 local B
= {M
= {}, T
= {}, I
= {}} -- bindings
49 local new
= {} -- constructors
51 local function range_by_pos(pos
)
52 return {start
= pos
, finish
= pos
}
55 function H
.A
.paste_reindent()
56 local clipboard
= ui
.clipboard_text
57 if not (clipboard
and #clipboard
> 0) then return end
58 buffer
:begin_undo_action()
60 local pos
= buffer
.selection_n_caret
[1] - 1
61 local cursor
= range_by_pos(pos
)
62 local _
, parent
= env
[buffer
].walker
.sexp_at(cursor
, true)
63 env
[buffer
].edit
.refmt_at(parent
, cursor
)
64 buffer
:end_undo_action()
67 local function is_comment(t
) return t
.is_comment
end
69 function H
.M
.backward(range
)
70 return env
[buffer
].walker
.start_before(range
, is_comment
)
73 function H
.M
.forward(range
)
74 return env
[buffer
].walker
.finish_after(range
, is_comment
)
77 function H
.M
.forward_up(range
)
78 return env
[buffer
].walker
.finish_up_after(range
)
81 function H
.M
.forward_down(range
)
82 return env
[buffer
].walker
.start_down_after(range
, is_comment
)
85 function H
.M
.backward_up(range
)
86 return env
[buffer
].walker
.start_up_before(range
)
89 function H
.M
.backward_down(range
)
90 return env
[buffer
].walker
.finish_down_before(range
, is_comment
)
93 function H
.M
.beginning_of_sentence(range
)
94 return env
[buffer
].walker
.anylist_start(range
)
97 function H
.M
.end_of_sentence(range
)
98 return env
[buffer
].walker
.anylist_finish(range
)
101 function H
.M
.next_section(range
)
102 local newpos
= env
[buffer
].walker
.find_after(range
, function(t
) return t
.section
and t
.start
> range
.start
end)
103 return newpos
and newpos
.start
or buffer
.length
106 function H
.M
.prev_section(range
)
107 local newpos
= env
[buffer
].walker
.find_before(range
, function(t
) return t
.section
end)
108 return newpos
and newpos
.start
or 0
111 function H
.M
.beginning_of_defun(range
)
112 local sexp
= env
[buffer
].walker
.paragraph_at(range
)
113 return sexp
and sexp
.start
116 function H
.M
.end_of_defun(range
)
117 local sexp
= env
[buffer
].walker
.paragraph_at(range
)
118 return sexp
and sexp
.finish
+ 1
121 function H
.M
.backward_word(range
)
122 -- XXX: ignore the second value:
123 return (env
[buffer
].walker
.start_float_before(range
, true))
126 function H
.M
.forward_word(range
)
127 -- XXX: ignore the second value:
128 return (env
[buffer
].walker
.finish_float_after(range
, true))
131 function H
.M
.prev_start(range
)
132 local escaped
= env
[buffer
].walker
.escaped_at(range
)
135 newpos
= env
[buffer
].walker
.start_word_before(escaped
, range
)
137 newpos
= env
[buffer
].walker
.start_before(range
, is_comment
)
142 function H
.M
.next_finish(range
)
143 local escaped
= env
[buffer
].walker
.escaped_at(range
)
146 newpos
= env
[buffer
].walker
.finish_word_after(escaped
, range
)
148 newpos
= env
[buffer
].walker
.finish_after(range
, is_comment
)
153 local function line_end(range
)
154 -- XXX: ignore the second value:
155 return (env
[buffer
].walker
.next_finish_wrapped(range
))
158 function H
.T
.line_end_extend(range
)
159 return range
.start
, line_end(range
)
162 local function line_begin(range
)
163 return env
[buffer
].walker
.prev_start_wrapped(range
)
166 function H
.T
.home_extend(range
)
167 return line_begin(range
), range
.finish
170 function H
.M
.back_to_indentation(_
, pos
)
171 return buffer
.line_indent_position
[buffer
:line_from_position(pos
+ 1)] - 1
174 function H
.T
.mark_sexp_backwards(range
)
175 local start
= H
.M
.prev_start(range
)
177 return start
, range
.finish
181 function H
.T
.mark_sexp(range
, pos
)
182 local backward
= pos
< range
.finish
184 local start
= H
.T
.mark_sexp_backwards(range
)
185 return range
.finish
, start
187 local finish
= H
.M
.next_finish(range
)
189 return range
.start
, finish
193 function H
.T
.paragraph(range
)
194 local sexps
= env
[buffer
].walker
.repl_line_at(range
)
196 return sexps
.start
, sexps
.finish
+ 1
200 function H
.T
.mark_defun(range
)
201 local sexp
= env
[buffer
].walker
.paragraph_at(range
)
202 if sexp
and not sexp
.is_comment
then
203 return sexp
.start
, sexp
.finish
+ 1
207 function H
.T
.expand_region(range
)
208 return env
[buffer
].walker
.wider_than(range
)
211 local function eval_repl_line(range
, pos
)
212 local sexps
= env
[buffer
].walker
.repl_line_at(range
)
213 return sexps
and H
.O
.eval_defun({start
= sexps
.start
, finish
= pos
}, pos
)
216 function H
.O
.format(range
, pos
)
217 local cursor
= range_by_pos(pos
)
218 -- XXX: don't call range_by_pos() from the argument list, as it returns a second value
219 return env
[buffer
].edit
.refmt_at(range
, cursor
) or pos
222 local function make_yank(handler
, fragments
)
223 return function(range
)
224 local txt
= buffer
:text_range(range
.start
+ 1, range
.finish
+ 1)
226 table.insert(fragments
, 1, txt
)
228 return handler
and handler(range
)
232 local function _delete(range
)
233 return buffer
:delete_range(range
.start
+ 1, range
.finish
- range
.start
)
236 function H
.O
.delete(range
, pos
)
238 local delete_maybe_yank
= do_copy
and make_yank(_delete
, fragments
) or _delete
239 local splicing
= true
240 return env
[buffer
].edit
.delete_splicing(range
, pos
, splicing
, delete_maybe_yank
), fragments
243 function H
.O
.change(range
, pos
)
245 local delete_maybe_yank
= do_copy
and make_yank(_delete
, fragments
) or _delete
246 return env
[buffer
].edit
.delete_nonsplicing(range
, pos
, delete_maybe_yank
), fragments
249 function H
.O
.yank(range
, pos
)
251 local yank
= make_yank(nil, fragments
)
252 local action
= {kill
= false, wrap
= true, splice
= false, func
= yank
}
253 env
[buffer
].edit
.pick_out(range
, pos
, action
)
254 return range
.start
, fragments
257 local function backward_char(range
)
258 return buffer
.position_before(range
.start
+ 1) - 1
261 local function forward_char(range
)
262 return buffer
.position_after(range
.start
+ 1) - 1
265 function H
.O
.wrap_round(range
, pos
)
266 return env
[buffer
].edit
.wrap_round(range
, pos
, M
.auto_square_brackets
)
269 function H
.O
.wrap_square(range
, pos
)
270 return env
[buffer
].edit
.wrap_square(range
, pos
, M
.auto_square_brackets
)
273 function H
.O
.wrap_curly(range
, pos
)
274 return env
[buffer
].edit
.wrap_curly(range
, pos
, M
.auto_square_brackets
)
277 function H
.O
.meta_doublequote(range
, pos
)
278 return env
[buffer
].edit
.meta_doublequote(range
, pos
, M
.auto_square_brackets
)
281 function H
.O
.wrap_comment(range
, pos
)
282 return env
[buffer
].edit
.wrap_comment(range
, pos
)
285 function H
.O
.toggle_comment(range
, pos
)
286 local sexp
= env
[buffer
].walker
.sexp_at(range
, true)
287 if sexp
and not sexp
.is_comment
then
288 return env
[buffer
].edit
.wrap_comment({start
= sexp
.start
, finish
= sexp
.finish
+ 1}, pos
)
290 return env
[buffer
].edit
.splice_sexp(range
, pos
)
294 function H
.O
.close_and_newline(range
)
295 return env
[buffer
].edit
.close_and_newline(range
)
298 function H
.O
.open_next_line(range
)
299 return env
[buffer
].edit
.close_and_newline(range
, "\n")
302 function H
.O
.raise_sexp(range
, pos
)
303 return env
[buffer
].edit
.raise_sexp(range
, pos
) or pos
306 function H
.O
.splice_sexp(range
, pos
)
307 return env
[buffer
].edit
.splice_anylist(range
, pos
)
310 function H
.O
.cycle_wrap(range
, pos
)
311 return env
[buffer
].edit
.cycle_wrap(range
, pos
)
314 function H
.O
.forward_slurp(range
)
315 return env
[buffer
].edit
.slurp_sexp(range
, true)
318 function H
.O
.backward_slurp(range
)
319 return env
[buffer
].edit
.slurp_sexp(range
)
322 function H
.O
.forward_barf(range
)
323 return env
[buffer
].edit
.barf_sexp(range
, true)
326 function H
.O
.backward_barf(range
)
327 return env
[buffer
].edit
.barf_sexp(range
)
330 function H
.O
.split_sexp(range
, pos
)
331 return env
[buffer
].edit
.split_anylist(range
) or pos
334 function H
.O
.join_sexps(range
, pos
)
335 return env
[buffer
].edit
.join_anylists(range
) or pos
338 function H
.O
.convolute_sexp(range
)
339 return env
[buffer
].edit
.convolute_lists(range
)
342 function H
.O
.transpose_sexps(range
, pos
)
343 return env
[buffer
].edit
.transpose_sexps(range
) or pos
346 function H
.O
.transpose_words(range
, pos
)
347 return env
[buffer
].edit
.transpose_words(range
) or pos
350 function H
.O
.transpose_chars(range
, pos
)
351 return env
[buffer
].edit
.transpose_chars(range
) or pos
356 function H
.O
.eval_defun(range
, pos
)
357 if not M
.repl_fifo
or range
.finish
== range
.start
then return pos
end
358 if pos
> range
.finish
then return pos
end
359 local unbalanced
= env
[buffer
].parser
.tree
.unbalanced_delimiters(range
)
360 if unbalanced
and #unbalanced
> 0 then return pos
end
362 if not repl_fifo
then
363 repl_fifo
, errmsg
= io
.open(M
.repl_fifo
, "a+")
366 repl_fifo
:write(buffer
:text_range(range
.start
+ 1, range
.finish
+ 1), "\n")
374 -- flush any code stuck in the fifo, so starting a REPL after the file has been closed won't read old stuff in.
375 events
.connect(events
.QUIT
, function()
378 repl_fifo
= io
.open(M
.repl_fifo
, "w+")
384 H
.I
.cut
= {H
.O
.change
, do_copy
= true}
385 H
.I
.copy
= {H
.O
.yank
, do_copy
= true --[[XXX: just so the clipboard is emptied first]]}
386 H
.I
.backward_delete
= {H
.O
.change
, backward_char
}
387 H
.I
.forward_delete
= {H
.O
.change
, forward_char
}
388 H
.I
.backward_kill_word
= {H
.O
.change
, H
.M
.backward_word
, do_copy
= true}
389 H
.I
.forward_kill_word
= {H
.O
.change
, H
.M
.forward_word
, do_copy
= true}
390 H
.I
.backward_kill_sexp
= {H
.O
.change
, H
.M
.prev_start
, do_copy
= true}
391 H
.I
.backward_kill_sexp1
= {H
.O
.change
, H
.M
.prev_start
}
392 H
.I
.kill_sexp
= {H
.O
.change
, H
.M
.next_finish
, do_copy
= true}
393 H
.I
.kill_sexp1
= {H
.O
.change
, H
.M
.next_finish
}
394 H
.I
.kill_sentence
= {H
.O
.change
, H
.M
.end_of_sentence
, do_copy
= true}
395 H
.I
.backward_kill_sentence
= {H
.O
.change
, H
.M
.beginning_of_sentence
, do_copy
= true}
396 H
.I
.backward_kill_line
= {H
.O
.change
, line_begin
, do_copy
= true}
397 H
.I
.backward_kill_line1
= {H
.O
.change
, line_begin
}
398 H
.I
.kill
= {H
.O
.change
, line_end
, do_copy
= true}
399 H
.I
.kill1
= {H
.O
.change
, line_end
}
400 H
.I
.splice_sexp_killing_backward
= {H
.O
.delete
, H
.M
.backward_up
, do_copy
= true}
401 H
.I
.splice_sexp_killing_backward1
= {H
.O
.delete
, H
.M
.backward_up
}
402 H
.I
.splice_sexp_killing_forward
= {H
.O
.delete
, H
.M
.forward_up
, do_copy
= true}
403 H
.I
.splice_sexp_killing_forward1
= {H
.O
.delete
, H
.M
.forward_up
}
404 H
.I
.raise_sexp
= {H
.O
.raise_sexp
}
405 H
.I
.splice_sexp
= {H
.O
.splice_sexp
}
406 H
.I
.forward_slurp
= {H
.O
.forward_slurp
}
407 H
.I
.forward_barf
= {H
.O
.forward_barf
}
408 H
.I
.backward_slurp
= {H
.O
.backward_slurp
}
409 H
.I
.backward_barf
= {H
.O
.backward_barf
}
410 H
.I
.convolute_sexp
= {H
.O
.convolute_sexp
}
411 H
.I
.wrap_round
= {H
.O
.wrap_round
, H
.M
.next_finish
}
412 H
.I
.wrap_square
= {H
.O
.wrap_square
, H
.M
.next_finish
}
413 H
.I
.wrap_curly
= {H
.O
.wrap_curly
, H
.M
.next_finish
}
414 H
.I
.meta_doublequote
= {H
.O
.meta_doublequote
, H
.M
.next_finish
}
415 H
.I
.wrap_comment
= {H
.O
.wrap_comment
, H
.M
.next_finish
}
416 H
.I
.enclose_round
= {H
.O
.wrap_round
, H
.T
.expand_region
}
417 H
.I
.enclose_square
= {H
.O
.wrap_square
, H
.T
.expand_region
}
418 H
.I
.enclose_curly
= {H
.O
.wrap_curly
, H
.T
.expand_region
}
419 H
.I
.enclose_doublequote
= {H
.O
.meta_doublequote
, H
.T
.expand_region
}
420 H
.I
.block_comment
= {H
.O
.toggle_comment
}
421 H
.I
.close_and_newline
= {H
.O
.close_and_newline
}
422 H
.I
.open_next_line
= {H
.O
.open_next_line
}
423 H
.I
.split_sexp
= {H
.O
.split_sexp
}
424 H
.I
.join_sexps
= {H
.O
.join_sexps
}
425 H
.I
.transpose_sexps
= {H
.O
.transpose_sexps
}
426 H
.I
.transpose_words
= {H
.O
.transpose_words
}
427 H
.I
.transpose_chars
= {H
.O
.transpose_chars
}
428 H
.I
.reindent_defun
= {H
.O
.format, H
.T
.mark_defun
}
429 H
.I
.eval_defun
= {H
.O
.eval_defun
, H
.T
.paragraph
}
430 H
.I
.eval_last_sexp
= {H
.O
.eval_defun
, H
.M
.backward
}
432 local function seek(selection
)
433 return function(offset
)
434 buffer
.selection_n_caret
[selection
] = offset
+ 1
435 buffer
.selection_n_anchor
[selection
] = offset
+ 1
441 for i
= 1, buffer
.selections
do
442 local pos
= buffer
.selection_n_caret
[i
] - 1
443 seek(i
)(func(range_by_pos(pos
), pos
) or pos
)
445 buffer
:scroll_range(buffer
.selection_n_caret
[1], buffer
.selection_n_caret
[buffer
.selections
])
446 if func
== H
.M
.prev_section
or func
== H
.M
.next_section
then
447 local align
= buffer
:line_from_position(buffer
.current_pos
) - buffer
.first_visible_line
448 buffer
.line_scroll(0, align
)
454 function new
.T(textobject
)
456 for i
= buffer
.selections
, 1, -1 do
457 local range
= {buffer
.selection_n_anchor
[i
] - 1, buffer
.selection_n_caret
[i
] - 1}
460 range
.start
, range
.finish
= range
[1], range
[2]
461 range
[1], range
[2] = nil, nil
462 local start
, finish
= textobject(range
, pos
)
463 if start
and finish
then
464 if buffer
.selections
== 1 then
465 buffer
:set_selection(finish
+ 1, start
+ 1)
467 buffer
:add_selection(finish
+ 1, start
+ 1)
474 function new
.I(params
)
475 local operator
, motion_or_textobject
= unpack(params
)
477 buffer
:begin_undo_action()
478 if params
.do_copy
then do_copy
= true end
480 for i
= buffer
.selections
, 1, -1 do
481 local pos
= buffer
.selection_n_caret
[i
] - 1
482 local anchor
= buffer
.selection_n_anchor
[i
] - 1
483 local range
= {start
= math
.min(anchor
, pos
), finish
= math
.max(anchor
, pos
)}
484 if motion_or_textobject
and (range
.finish
== range
.start
) then
485 local start
, finish
= motion_or_textobject(range
, pos
)
486 if start
and not finish
then
487 range
= {start
= math
.min(pos
, start
), finish
= math
.max(pos
, start
)}
488 elseif start
and finish
then
489 range
= {start
= start
, finish
= finish
}
492 local newpos
, fragments
= operator(range
, pos
)
493 if params
.do_copy
and fragments
and #fragments
> 0 then
494 for _
, v
in ipairs(fragments
) do
495 table.insert(clips
, v
)
498 seek(i
)(newpos
or pos
)
501 if params
.do_copy
and #clips
> 0 then
502 local clipboard
= table.concat(clips
)
503 buffer
:copy_text(clipboard
)
505 buffer
:end_undo_action()
510 local function mnemonics_off()
511 if CURSES
then return end
512 if not initialized
then return end -- wait for the storm of events to pass
513 local filetype
= buffer
.lexer_language
514 if not supported
[filetype
] then return end
515 orig
.menus
= orig
.menus
or {}
516 for _
, menu
in ipairs(textadept
.menu
.menubar
) do
517 local mnemonic
= menu
.title
:match
"&%a"
518 mnemonic
= mnemonic
and mnemonic
:sub(2, 2):lower()
519 if mnemonic
and keys
[filetype
] and keys
[filetype
][ALT
..mnemonic
] then
520 local new_title
= menu
.title
:gsub("&", "", 1)
521 orig
.menus
[new_title
] = menu
.title
522 menu
.title
= new_title
527 local function mnemonics_on()
528 if CURSES
then return end
529 if not initialized
then return end -- wait for the storm of events to pass
530 local filetype
= buffer
.lexer_language
531 if not supported
[filetype
] then return end
532 for _
, menu
in ipairs(textadept
.menu
.menubar
) do
533 if orig
.menus
[menu
.title
] then
534 menu
.title
= orig
.menus
[menu
.title
]
539 local extended_identifier_chars
= "!$%&*+-./:<=>?@^_~"
541 local function settings_backup()
542 if not initialized
then return end -- wait for the storm of events to pass
543 local filetype
= buffer
.lexer_language
544 if not supported
[filetype
] then return end
545 if not buffer
.word_chars
:find(extended_identifier_chars
, 1, true) then
546 buffer
.word_chars
= extended_identifier_chars
.. buffer
.word_chars
548 orig
.auto_indent
= textadept
.editing
.auto_indent
549 orig
.strip_trailing_spaces
= textadept
.editing
.strip_trailing_spaces
550 orig
.auto_pairs
= textadept
.editing
.auto_pairs
551 orig
.typeover_auto_paired
= textadept
.editing
.typeover_auto_paired
553 textadept
.editing
.auto_indent
= false
554 textadept
.editing
.strip_trailing_spaces
= false
555 textadept
.editing
.auto_pairs
= nil
556 textadept
.editing
.typeover_auto_paired
= nil
559 local function settings_restore()
560 if not initialized
then return end -- wait for the storm of events to pass
561 local filetype
= buffer
.lexer_language
562 if not supported
[filetype
] then return end
563 textadept
.editing
.auto_indent
= orig
.auto_indent
564 textadept
.editing
.strip_trailing_spaces
= orig
.strip_trailing_spaces
565 textadept
.editing
.auto_pairs
= orig
.auto_pairs
566 textadept
.editing
.typeover_auto_paired
= orig
.typeover_auto_paired
569 local function setup() mnemonics_off() settings_backup() end
570 local function teardown() mnemonics_on() settings_restore() end
572 events
.connect(events
.BUFFER_BEFORE_SWITCH
, function() teardown() end)
573 events
.connect(events
.BUFFER_AFTER_SWITCH
, function() setup() end)
574 events
.connect(events
.VIEW_BEFORE_SWITCH
, function() teardown() end)
575 events
.connect(events
.VIEW_AFTER_SWITCH
, function() setup() end)
577 events
.connect(events
.INITIALIZED
, function()
578 for kind
, register_new
in pairs(new
) do
579 for name
, handler
in pairs(H
[kind
]) do
580 B
[kind
][name
] = register_new(handler
)
583 local suffix
= CURSES
and "-curses" or ""
584 local keyconfig
= {require(cwd
..".keytheme.CUA"..suffix
)(B
, H
),
585 M
.emacs
and require(cwd
..".keytheme.emacs"..suffix
)(B
, H
) or nil}
587 for _
, config
in ipairs(keyconfig
) do
588 for func
, key
in pairs(config
) do
589 if type(key
) == "table" then
590 for _
, k
in ipairs(key
) do
593 for k
, j
in pairs(key
) do
594 if type(k
) ~= "number" and (not inverted
[k
] or type(inverted
[k
]) ~= "table") then
597 if type(inverted
[k
]) == "table" then
598 inverted
[k
][j
] = func
606 for dialect
in pairs(supported
) do
607 if not keys
[dialect
] then keys
[dialect
] = {} end
608 for key
, binding
in pairs(inverted
) do
609 keys
[dialect
][key
] = binding
616 events
.connect(events
.FILE_OPENED
, function()
617 local filetype
= buffer
.lexer_language
618 if not supported
[filetype
] then return end
619 local function write(pos
, txt
)
620 buffer
:set_target_range(pos
+ 1, pos
+ 1)
621 buffer
:replace_target(txt
)
623 local function delete(pos
, len
)
624 return buffer
:delete_range(pos
+ 1, len
)
626 local function read(base
, len
)
627 if base
== buffer
.length
- 1 then return end
628 base
= (base
or 0) + 1
629 len
= len
or buffer
.length
630 local more
= base
+ len
<= buffer
.length
631 return buffer
:text_range(base
, base
+ len
), more
633 -- TODO: this can be implemented in the core modules, without using editor-specific APIs:
634 local function eol_at(pos
)
635 return buffer
.line_end_position
[buffer
:line_from_position(pos
+ 1)] - 1
637 -- TODO: this can be implemented in the core modules, without using editor-specific APIs:
638 local function bol_at(pos
)
639 return buffer
:position_from_line(buffer
:line_from_position(pos
+ 1)) - 1
641 env
[buffer
] = init(filetype
, read, write, delete
, eol_at
, bol_at
)
642 local consumed_char
= {"\n", " "}
643 for ch
in pairs(env
[buffer
].parser
.opposite
) do
645 table.insert(consumed_char
, ch
)
648 env
[buffer
].consumed_char
= "[" .. table.concat(consumed_char
, "%") .. "]"
652 events
.connect(events
.BUFFER_DELETED
, function()
653 for b
in pairs(env
) do
654 if not _BUFFERS
[b
] then
660 events
.connect(events
.MODIFIED
, function(pos
, mtype
)
661 local filetype
= buffer
.lexer_language
662 if not supported
[filetype
] then return end
663 local text_inserted
, text_deleted
= 0x01, 0x02
664 if 0 == (mtype
& (text_inserted | text_deleted
)) then return end
665 if env
[buffer
].parser
.tree
.is_parsed(pos
- 1) then
666 env
[buffer
].parser
.tree
.rewind(pos
- 1)
670 events
.connect(events
.KEYPRESS
, function(key
)
671 if ui
.command_entry
.active
then return end
672 local filetype
= buffer
.lexer_language
673 if not supported
[filetype
] then return end
674 if key
:find(CTRL
) or key
:find(ALT
) or key
:find(SHIFT
) then return end
676 local winput
= env
[buffer
].input
677 local consumed_char
= key
:find(env
[buffer
].consumed_char
)
678 if consumed_char
then buffer
:begin_undo_action() end
679 for i
= buffer
.selections
, 1, -1 do
680 local anchor
, caret
= buffer
.selection_n_anchor
[i
] - 1, buffer
.selection_n_caret
[i
] - 1
681 local range
= {start
= math
.min(anchor
, caret
), finish
= math
.max(anchor
, caret
)}
683 local sexp
, parent
= env
[buffer
].walker
.sexp_at(range
, true)
684 if parent
.is_root
and (not sexp
or (not sexp
.is_comment
and sexp
.finish
+ 1 == range
.start
)) then
685 local same_line
= not not sexp
687 -- FIXME: this assumes only one selection:
688 local line
, pos
= buffer
.get_cur_line(), buffer
.current_pos
- 1
689 local prev_finish
= env
[buffer
].walker
.finish_before(range
)
692 same_line
= line
== buffer
.get_cur_line()
696 if sexp
or same_line
then
697 eval_repl_line(range
, buffer
.selection_n_caret
[i
] - 1)
701 ret
= winput
.insert(range
, seek(i
), key
, M
.auto_square_brackets
)
703 if consumed_char
then buffer
:end_undo_action() end