textbringer 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +22 -8
- data/CHANGES.md +11 -0
- data/Guardfile +1 -1
- data/exe/tbtags +26 -0
- data/exe/textbringer +1 -0
- data/lib/textbringer.rb +6 -0
- data/lib/textbringer/buffer.rb +158 -83
- data/lib/textbringer/commands.rb +18 -6
- data/lib/textbringer/commands/buffers.rb +187 -43
- data/lib/textbringer/commands/clipboard.rb +23 -6
- data/lib/textbringer/commands/ctags.rb +3 -23
- data/lib/textbringer/commands/files.rb +10 -7
- data/lib/textbringer/commands/help.rb +114 -0
- data/lib/textbringer/commands/isearch.rb +13 -7
- data/lib/textbringer/commands/keyboard_macro.rb +86 -0
- data/lib/textbringer/commands/misc.rb +43 -2
- data/lib/textbringer/commands/register.rb +128 -0
- data/lib/textbringer/commands/replace.rb +4 -3
- data/lib/textbringer/commands/windows.rb +49 -22
- data/lib/textbringer/config.rb +2 -1
- data/lib/textbringer/controller.rb +89 -23
- data/lib/textbringer/keymap.rb +60 -2
- data/lib/textbringer/modes/help_mode.rb +42 -0
- data/lib/textbringer/modes/programming_mode.rb +35 -28
- data/lib/textbringer/modes/ruby_mode.rb +28 -10
- data/lib/textbringer/plugin.rb +21 -0
- data/lib/textbringer/ring.rb +84 -0
- data/lib/textbringer/utils.rb +28 -0
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +59 -5
- data/textbringer.gemspec +1 -1
- metadata +12 -4
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Textbringer
|
4
|
+
class HelpMode < Mode
|
5
|
+
define_generic_command :jump_to_link
|
6
|
+
|
7
|
+
HELP_MODE_MAP = Keymap.new
|
8
|
+
HELP_MODE_MAP.define_key(?\C-m, :jump_to_link_command)
|
9
|
+
HELP_MODE_MAP.define_key(?l, :help_go_back)
|
10
|
+
HELP_MODE_MAP.define_key("\C-c\C-b", :help_go_back)
|
11
|
+
HELP_MODE_MAP.define_key(?r, :help_go_forward)
|
12
|
+
HELP_MODE_MAP.define_key("\C-c\C-f", :help_go_forward)
|
13
|
+
|
14
|
+
define_syntax :link, /
|
15
|
+
(?: ^\S*?:\d+$ ) |
|
16
|
+
(?: \[[_a-zA-Z][_a-zA-Z0-9]*\] )
|
17
|
+
/x
|
18
|
+
|
19
|
+
def initialize(buffer)
|
20
|
+
super(buffer)
|
21
|
+
buffer.keymap = HELP_MODE_MAP
|
22
|
+
end
|
23
|
+
|
24
|
+
def jump_to_link
|
25
|
+
@buffer.save_excursion do
|
26
|
+
@buffer.skip_re_backward(/[_a-zA-Z0-9]/)
|
27
|
+
if @buffer.char_before == ?[ &&
|
28
|
+
@buffer.looking_at?(/([_a-zA-Z][_a-zA-Z0-9]*)\]/)
|
29
|
+
describe_command(match_string(1))
|
30
|
+
else
|
31
|
+
@buffer.beginning_of_line
|
32
|
+
if @buffer.looking_at?(/^(\S*?):(\d+)$/)
|
33
|
+
file_name = @buffer.match_string(1)
|
34
|
+
line_number = @buffer.match_string(2).to_i
|
35
|
+
find_file(file_name)
|
36
|
+
goto_line(line_number)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -6,7 +6,8 @@ module Textbringer
|
|
6
6
|
undefine_command(:programming_mode)
|
7
7
|
|
8
8
|
define_generic_command :indent_line
|
9
|
-
define_generic_command :
|
9
|
+
define_generic_command :reindent_then_newline_and_indent
|
10
|
+
define_generic_command :indent_region
|
10
11
|
define_generic_command :forward_definition
|
11
12
|
define_generic_command :backward_definition
|
12
13
|
define_generic_command :compile
|
@@ -14,7 +15,9 @@ module Textbringer
|
|
14
15
|
|
15
16
|
PROGRAMMING_MODE_MAP = Keymap.new
|
16
17
|
PROGRAMMING_MODE_MAP.define_key("\t", :indent_line_command)
|
17
|
-
PROGRAMMING_MODE_MAP.define_key("\C-m",
|
18
|
+
PROGRAMMING_MODE_MAP.define_key("\C-m",
|
19
|
+
:reindent_then_newline_and_indent_command)
|
20
|
+
PROGRAMMING_MODE_MAP.define_key("\e\C-\\", :indent_region_command)
|
18
21
|
PROGRAMMING_MODE_MAP.define_key("\C-c\C-n", :forward_definition_command)
|
19
22
|
PROGRAMMING_MODE_MAP.define_key("\C-c\C-p", :backward_definition_command)
|
20
23
|
PROGRAMMING_MODE_MAP.define_key("\C-c\C-c", :compile_command)
|
@@ -32,18 +35,16 @@ module Textbringer
|
|
32
35
|
return result if level.nil?
|
33
36
|
@buffer.save_excursion do
|
34
37
|
@buffer.beginning_of_line
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
if has_space
|
46
|
-
@buffer.merge_undo(2)
|
38
|
+
@buffer.composite_edit do
|
39
|
+
if @buffer.looking_at?(/[ \t]+/)
|
40
|
+
s = @buffer.match_string(0)
|
41
|
+
break if /\t/ !~ s && s.size == level
|
42
|
+
@buffer.delete_region(@buffer.match_beginning(0),
|
43
|
+
@buffer.match_end(0))
|
44
|
+
else
|
45
|
+
break if level == 0
|
46
|
+
end
|
47
|
+
@buffer.indent_to(level)
|
47
48
|
end
|
48
49
|
result = true
|
49
50
|
end
|
@@ -56,24 +57,30 @@ module Textbringer
|
|
56
57
|
result
|
57
58
|
end
|
58
59
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
def reindent_then_newline_and_indent
|
61
|
+
@buffer.composite_edit do
|
62
|
+
indent_line
|
63
|
+
@buffer.save_excursion do
|
64
|
+
pos = @buffer.point
|
65
|
+
@buffer.beginning_of_line
|
66
|
+
if /\A[ \t]+\z/ =~ @buffer.substring(@buffer.point, pos)
|
67
|
+
@buffer.delete_region(@buffer.point, pos)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
@buffer.insert("\n")
|
71
|
+
indent_line
|
63
72
|
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def indent_region(s = @buffer.mark, e = @buffer.point)
|
76
|
+
s, e = Buffer.region_boundaries(s, e)
|
64
77
|
@buffer.save_excursion do
|
65
|
-
|
66
|
-
@buffer.
|
67
|
-
|
68
|
-
@buffer.
|
69
|
-
n += 1
|
78
|
+
@buffer.goto_char(s)
|
79
|
+
while @buffer.point < e
|
80
|
+
indent_line
|
81
|
+
@buffer.forward_line
|
70
82
|
end
|
71
83
|
end
|
72
|
-
@buffer.insert("\n")
|
73
|
-
if indent_line
|
74
|
-
n += 1
|
75
|
-
end
|
76
|
-
@buffer.merge_undo(n) if n > 1
|
77
84
|
end
|
78
85
|
|
79
86
|
private
|
@@ -27,13 +27,24 @@ module Textbringer
|
|
27
27
|
/x
|
28
28
|
|
29
29
|
define_syntax :string, /
|
30
|
-
(?: (?<! [a-zA-Z] ) \?
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
(?: (?<! [a-zA-Z] ) \?
|
31
|
+
(:?
|
32
|
+
[^\\\s] |
|
33
|
+
\\ [0-7]{1,3} |
|
34
|
+
\\x [0-9a-fA-F]{2} |
|
35
|
+
\\u [0-9a-fA-F]{4} |
|
36
|
+
\\u \{ [0-9a-fA-F]+ \} |
|
37
|
+
\\C - . |
|
38
|
+
\\M - . |
|
39
|
+
\\ .
|
40
|
+
)
|
41
|
+
) |
|
42
|
+
(?: %[qQrwWsiIx]?\{ (?: [^\\}] | \\ . )* \} ) |
|
43
|
+
(?: %[qQrwWsiIx]?\( (?: [^\\)] | \\ . )* \) ) |
|
44
|
+
(?: %[qQrwWsiIx]?\[ (?: [^\\\]] | \\ . )* \] ) |
|
45
|
+
(?: %[qQrwWsiIx]?< (?: [^\\>] | \\ . )* > ) |
|
35
46
|
(?:
|
36
|
-
%[
|
47
|
+
%[qQrwWsiIx]?
|
37
48
|
(?<string_delimiter>[^{(\[<a-zA-Z0-9\s\u{0100}-\u{10ffff}])
|
38
49
|
(?: (?! \k<string_delimiter> ) [^\\] | \\ . )*
|
39
50
|
\k<string_delimiter>
|
@@ -61,13 +72,18 @@ module Textbringer
|
|
61
72
|
) |
|
62
73
|
(?:
|
63
74
|
(?<! class | class \s | [\]})"'.] | :: | \w )
|
64
|
-
|
75
|
+
<<[\-~]?(?<heredoc_quote>['"`]?)
|
65
76
|
(?<heredoc_terminator>
|
66
|
-
[_a-zA-Z\u{0100}-\u{10ffff}]
|
67
|
-
|
77
|
+
(?> [_a-zA-Z\u{0100}-\u{10ffff}]
|
78
|
+
[_a-zA-Z0-9\u{0100}-\u{10ffff}]* )
|
68
79
|
)
|
69
80
|
\k<heredoc_quote>
|
70
|
-
(?> (?:.|\n)*? \k<heredoc_terminator> )
|
81
|
+
(?> (?:.|\n)*? ^ [\ \t]* \k<heredoc_terminator> $ )
|
82
|
+
) |
|
83
|
+
(?:
|
84
|
+
(?<! : ) :
|
85
|
+
[_a-zA-Z\u{0100}-\u{10ffff}]
|
86
|
+
[_a-zA-Z0-9\u{0100}-\u{10ffff}]*
|
71
87
|
)
|
72
88
|
/x
|
73
89
|
|
@@ -256,6 +272,8 @@ module Textbringer
|
|
256
272
|
(line, column), event, text = tokens[i]
|
257
273
|
case event
|
258
274
|
when :on_kw
|
275
|
+
_, prev_event, _ = tokens[i - 1]
|
276
|
+
next if prev_event == :on_symbeg
|
259
277
|
case text
|
260
278
|
when "class", "module", "def", "if", "unless", "case",
|
261
279
|
"do", "for", "while", "until", "begin"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Textbringer
|
4
|
+
module Plugin
|
5
|
+
def self.load_plugins
|
6
|
+
files = Gem.find_files("textbringer_plugin.rb")
|
7
|
+
files.group_by { |file|
|
8
|
+
file.slice(/([^\/]+)-[\w.]+\/lib\/textbringer_plugin\.rb\z/, 1)
|
9
|
+
}.map { |gem, versions|
|
10
|
+
versions.sort_by { |version|
|
11
|
+
v = version.slice(/[^\/]+-([\w.]+)\/lib\/textbringer_plugin\.rb\z/,
|
12
|
+
1)
|
13
|
+
Gem::Version.create(v)
|
14
|
+
}.last
|
15
|
+
}.each do |file|
|
16
|
+
load(file)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Textbringer
|
4
|
+
class Ring
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(max = 30, on_delete: ->(x) {})
|
8
|
+
@max = max
|
9
|
+
@ring = []
|
10
|
+
@current = -1
|
11
|
+
@on_delete = on_delete
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear
|
15
|
+
@ring.clear
|
16
|
+
@current = -1
|
17
|
+
end
|
18
|
+
|
19
|
+
def push(obj)
|
20
|
+
@current += 1
|
21
|
+
if @ring.size < @max
|
22
|
+
@ring.insert(@current, obj)
|
23
|
+
else
|
24
|
+
if @current == @max
|
25
|
+
@current = 0
|
26
|
+
end
|
27
|
+
@on_delete.call(@ring[@current])
|
28
|
+
@ring[@current] = obj
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def pop
|
33
|
+
x = @ring[@current]
|
34
|
+
rotate(1)
|
35
|
+
x
|
36
|
+
end
|
37
|
+
|
38
|
+
def current
|
39
|
+
if @ring.empty?
|
40
|
+
raise EditorError, "Ring is empty"
|
41
|
+
end
|
42
|
+
@ring[@current]
|
43
|
+
end
|
44
|
+
|
45
|
+
def rotate(n)
|
46
|
+
@current = get_index(n)
|
47
|
+
@ring[@current]
|
48
|
+
end
|
49
|
+
|
50
|
+
def [](n = 0)
|
51
|
+
@ring[get_index(n)]
|
52
|
+
end
|
53
|
+
|
54
|
+
def empty?
|
55
|
+
@ring.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
def size
|
59
|
+
@ring.size
|
60
|
+
end
|
61
|
+
|
62
|
+
def each(&block)
|
63
|
+
@ring.each(&block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_a
|
67
|
+
@ring.to_a
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def get_index(n)
|
73
|
+
if @ring.empty?
|
74
|
+
raise EditorError, "Ring is empty"
|
75
|
+
end
|
76
|
+
i = @current - n
|
77
|
+
if 0 <= i && i < @ring.size
|
78
|
+
i
|
79
|
+
else
|
80
|
+
i % @ring.size
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/textbringer/utils.rb
CHANGED
@@ -231,6 +231,34 @@ module Textbringer
|
|
231
231
|
read_from_minibuffer(prompt + " (#{char_options}) ", keymap: map)
|
232
232
|
end
|
233
233
|
|
234
|
+
def read_key_sequence(prompt)
|
235
|
+
buffer = Buffer.current
|
236
|
+
key_sequence = []
|
237
|
+
map = Keymap.new
|
238
|
+
map.define_key("\C-g", :abort_recursive_edit)
|
239
|
+
map.handle_undefined_key do |key|
|
240
|
+
-> {
|
241
|
+
key_sequence.push(key)
|
242
|
+
cmd = buffer.keymap&.lookup(key_sequence) ||
|
243
|
+
GLOBAL_MAP.lookup(key_sequence)
|
244
|
+
if !cmd.is_a?(Keymap)
|
245
|
+
exit_recursive_edit
|
246
|
+
end
|
247
|
+
Buffer.current.clear
|
248
|
+
keys = Keymap.key_sequence_string(key_sequence)
|
249
|
+
Buffer.current.insert("#{keys}-")
|
250
|
+
}
|
251
|
+
end
|
252
|
+
read_from_minibuffer(prompt, keymap: map)
|
253
|
+
if buffer.keymap&.lookup(key_sequence) ||
|
254
|
+
GLOBAL_MAP.lookup(key_sequence)
|
255
|
+
key_sequence
|
256
|
+
else
|
257
|
+
keys = Keymap.key_sequence_string(key_sequence)
|
258
|
+
raise EditorError, "#{keys} is undefined"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
234
262
|
HOOKS = Hash.new { |h, k| h[k] = [] }
|
235
263
|
|
236
264
|
def add_hook(name, func)
|
data/lib/textbringer/version.rb
CHANGED
data/lib/textbringer/window.rb
CHANGED
@@ -184,6 +184,7 @@ module Textbringer
|
|
184
184
|
end
|
185
185
|
|
186
186
|
def self.redisplay
|
187
|
+
return if Controller.current.executing_keyboard_macro?
|
187
188
|
return if Window.current.has_input?
|
188
189
|
@@windows.each do |window|
|
189
190
|
window.redisplay unless window.current?
|
@@ -615,6 +616,25 @@ module Textbringer
|
|
615
616
|
end
|
616
617
|
end
|
617
618
|
|
619
|
+
def shrink(n)
|
620
|
+
enlarge(-n)
|
621
|
+
end
|
622
|
+
|
623
|
+
def shrink_if_larger_than_buffer
|
624
|
+
@buffer.save_point do
|
625
|
+
@buffer.end_of_buffer
|
626
|
+
@buffer.skip_re_backward(/\s/)
|
627
|
+
count = beginning_of_line_and_count(Window.lines) + 1
|
628
|
+
while !@buffer.beginning_of_buffer?
|
629
|
+
@buffer.backward_char
|
630
|
+
count += beginning_of_line_and_count(Window.lines) + 1
|
631
|
+
end
|
632
|
+
if lines - 1 > count
|
633
|
+
shrink(lines - 1 - count)
|
634
|
+
end
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
618
638
|
private
|
619
639
|
|
620
640
|
def initialize_window(num_lines, num_columns, y, x)
|
@@ -802,6 +822,7 @@ module Textbringer
|
|
802
822
|
|
803
823
|
def clear
|
804
824
|
@buffer.clear
|
825
|
+
@top_of_window.location = @buffer.point_min
|
805
826
|
@message = nil
|
806
827
|
@prompt = ""
|
807
828
|
end
|
@@ -824,19 +845,25 @@ module Textbringer
|
|
824
845
|
else
|
825
846
|
prompt = escape(@prompt)
|
826
847
|
@window.addstr(prompt)
|
848
|
+
framer
|
849
|
+
@buffer.point_to_mark(@top_of_window)
|
827
850
|
y = x = 0
|
828
|
-
columns = @columns - Buffer.display_width(prompt)
|
829
|
-
beginning_of_line_and_count(1, columns)
|
830
851
|
while !@buffer.end_of_buffer?
|
852
|
+
cury, curx = @window.cury, @window.curx
|
831
853
|
if @buffer.point_at_mark?(saved)
|
832
|
-
y, x =
|
854
|
+
y, x = cury, curx
|
833
855
|
end
|
834
856
|
c = @buffer.char_after
|
835
857
|
if c == "\n"
|
836
858
|
break
|
837
859
|
end
|
838
|
-
|
839
|
-
|
860
|
+
s = escape(c)
|
861
|
+
newx = curx + Buffer.display_width(s)
|
862
|
+
if newx > @columns
|
863
|
+
break
|
864
|
+
end
|
865
|
+
@window.addstr(s)
|
866
|
+
break if newx >= @columns
|
840
867
|
@buffer.forward_char
|
841
868
|
end
|
842
869
|
if @buffer.point_at_mark?(saved)
|
@@ -869,5 +896,32 @@ module Textbringer
|
|
869
896
|
def initialize_window(num_lines, num_columns, y, x)
|
870
897
|
@window = Curses::Window.new(num_lines, num_columns, y, x)
|
871
898
|
end
|
899
|
+
|
900
|
+
def escape(s)
|
901
|
+
super(s).gsub(/\t/, "^I")
|
902
|
+
end
|
903
|
+
|
904
|
+
def framer
|
905
|
+
@buffer.save_point do |saved|
|
906
|
+
max_width = @columns - @window.curx
|
907
|
+
width = 0
|
908
|
+
loop do
|
909
|
+
c = @buffer.char_after
|
910
|
+
if c.nil?
|
911
|
+
width += 1
|
912
|
+
else
|
913
|
+
width += Buffer.display_width(escape(c))
|
914
|
+
end
|
915
|
+
if width > max_width
|
916
|
+
@buffer.forward_char
|
917
|
+
break
|
918
|
+
elsif width == max_width || @buffer.beginning_of_line?
|
919
|
+
break
|
920
|
+
end
|
921
|
+
@buffer.backward_char
|
922
|
+
end
|
923
|
+
@top_of_window.location = @buffer.point
|
924
|
+
end
|
925
|
+
end
|
872
926
|
end
|
873
927
|
end
|