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.
@@ -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 :newline_and_reindent
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", :newline_and_reindent_command)
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
- has_space = @buffer.looking_at?(/[ \t]+/)
36
- if has_space
37
- s = @buffer.match_string(0)
38
- break if /\t/ !~ s && s.size == level
39
- @buffer.delete_region(@buffer.match_beginning(0),
40
- @buffer.match_end(0))
41
- else
42
- break if level == 0
43
- end
44
- @buffer.indent_to(level)
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 newline_and_reindent
60
- n = 1
61
- if indent_line
62
- n += 1
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
- pos = @buffer.point
66
- @buffer.beginning_of_line
67
- if /\A[ \t]+\z/ =~ @buffer.substring(@buffer.point, pos)
68
- @buffer.delete_region(@buffer.point, pos)
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] ) \? (:? [^\\\s] | \\ . ) ) |
31
- (?: %[qQrwWsix]?\{ (?: [^\\}] | \\ . )* \} ) |
32
- (?: %[qQrwWsix]?\( (?: [^\\)] | \\ . )* \) ) |
33
- (?: %[qQrwWsix]?\[ (?: [^\\\]] | \\ . )* \] ) |
34
- (?: %[qQrwWsix]?< (?: [^\\>] | \\ . )* > ) |
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
- %[qQrwWsix]?
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
- <<-?(?<heredoc_quote>['"`]?)
75
+ <<[\-~]?(?<heredoc_quote>['"`]?)
65
76
  (?<heredoc_terminator>
66
- [_a-zA-Z\u{0100}-\u{10ffff}]
67
- [_a-zA-Z0-9\u{0100}-\u{10ffff}]*
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
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module Textbringer
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
@@ -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 = @window.cury, @window.curx
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
- @window.addstr(escape(c))
839
- break if @window.curx == @columns
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