textbringer 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|