textbringer 24 → 26

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7426dda13275f37439bdcc360a55971b34cc4b62916eeaee734b127ab9a8a88
4
- data.tar.gz: fcc6391ac0697659a8b85005766186502731e8473928a5e5165d7c0a97d533f1
3
+ metadata.gz: e2675c1e38b9c3e24666f87ebf58f4cede5bca7dfec811eec58cee700a500f33
4
+ data.tar.gz: b1835247ba5c703983dea5b80a84fb26b0f43954ee1cb9b737ac82e23a843278
5
5
  SHA512:
6
- metadata.gz: be219b93c87935b98780c5d41430dd3d0760b839e20977dc843fa88aa8253614fb0e68dc7f90e37c08de6a23722daec928294f9702114908707011c33c766dc6
7
- data.tar.gz: 6f61d4ed9ce65308bf150f24c886e05a1743af8bd38913c80f750db77c64a90b543ae95f60774b9d8ae52953cfa91e5f36e62665e0830b9aab6f6d97925ab289
6
+ metadata.gz: 7bb45438e1cf3912540446ceeed8dcbbbfc1ac4c796242a7d63783e0741223051d4edd200aaa2aa5a1f0c8d01c0b7a8c94f3a9f576b303e29f6303357ae3f2b8
7
+ data.tar.gz: 30ed7d5c79192c5bfdf81122d018ae9269e30087b24be951a7d795c5a0e92aaf84aa7a9f2d58d476ed241e57b44d39ff15284549249127ac3ab811e79ce096ac
data/README.ja.md CHANGED
@@ -58,6 +58,14 @@ Textbringerが依存するcurses.gemをインストールする前に、ncursesw
58
58
  mod_meta_key = alt
59
59
  mod_meta_mode = esc
60
60
 
61
+ ### True color (24ビットカラー)
62
+
63
+ 端末エミュレータがtrue colorに対応している場合、`TERM`に`xterm-direct`を設定すると24ビットカラーが有効になります。
64
+
65
+ $ TERM=xterm-direct txtb
66
+
67
+ テーマで指定されたhex色が256色パレットに近似されず、そのまま表示されるようになります。
68
+
61
69
  ### 東アジアの曖昧な文字幅
62
70
 
63
71
  [曖昧な文字](http://unicode.org/reports/tr11/#Ambiguous)を全角扱いするには、以下の設定を~/.textbringer.rbに記述してください。
@@ -91,6 +99,22 @@ xterm、 mlterm、screenにはそれぞれ独自の設定項目があります
91
99
 
92
100
  cjkwidth on
93
101
 
102
+ ## テーマ
103
+
104
+ `Alt+x load_theme RET` で[textbringer/themes](lib/textbringer/themes)または~/.textbringer/themes内のテーマを読み込むことができます。
105
+
106
+ ~/.textbringer.rbでテーマを設定することもできます。
107
+
108
+ ```ruby
109
+ load_theme "catppuccin"
110
+ ```
111
+
112
+ テーマが独自の背景色を設定している場合に、端末のデフォルト背景色を使いたいときは`load_theme`の後に以下を追加してください。
113
+
114
+ ```ruby
115
+ set_background_color "default"
116
+ ```
117
+
94
118
  ## プラグイン
95
119
 
96
120
  * [Mournmail](https://github.com/shugo/mournmail): 電子メールクライアント
data/README.md CHANGED
@@ -60,6 +60,14 @@ Add the following lines to ~/.mlterm/main.
60
60
  mod_meta_key = alt
61
61
  mod_meta_mode = esc
62
62
 
63
+ ### True color (24-bit color)
64
+
65
+ If your terminal emulator supports true color, set `TERM` to `xterm-direct` to enable 24-bit color.
66
+
67
+ $ TERM=xterm-direct txtb
68
+
69
+ This allows themes to display their exact hex colors instead of approximating to the 256-color palette.
70
+
63
71
  ### East asian ambiguous width
64
72
 
65
73
  Add the following line to ~/.textbringer.rb to treat
@@ -103,6 +111,22 @@ Add the following line to ~/.screenrc.
103
111
  * [textbringer-presentation](https://github.com/shugo/textbringer-presentation): a presentation tool
104
112
  * [textbringer-ghost_text](https://github.com/shugo/textbringer-ghost_text): a [GhostText](https://github.com/fregante/GhostText) plugin
105
113
 
114
+ ## Themes
115
+
116
+ Type `Alt+x load_theme RET` to load a theme from [textbringer/themes](lib/textbringer/themes) or ~/.textbringer/themes.
117
+
118
+ Themes can also be loaded in ~/.textbringer.rb.
119
+
120
+ ```ruby
121
+ load_theme "catppuccin"
122
+ ```
123
+
124
+ If a theme sets its own background color but you prefer to use the terminal's default background, add the following after `load_theme`:
125
+
126
+ ```ruby
127
+ set_background_color "default"
128
+ ```
129
+
106
130
  ## Development
107
131
 
108
132
  After checking out the repo, run `bundle install` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/exe/txtb CHANGED
@@ -33,6 +33,7 @@ begin
33
33
  transient_mark_mode(true)
34
34
  Window.echo_area.clear_message
35
35
  Plugin.load_plugins
36
+ Theme.load_default if Window.has_colors?
36
37
  load_user_config("~/.textbringer.rb")
37
38
  ruby_mode
38
39
  if ARGV.size > 0
@@ -9,7 +9,8 @@ module Textbringer
9
9
  extend Enumerable
10
10
 
11
11
  attr_accessor :mode, :keymap
12
- attr_reader :name, :file_name, :file_encoding, :file_format, :point, :marks
12
+ attr_reader :name, :file_name, :file_encoding, :file_format, :point, :marks,
13
+ :file_version, :version
13
14
  attr_reader :current_line, :current_column, :visible_mark, :isearch_mark, :mark_active
14
15
  attr_reader :last_match
15
16
  attr_reader :input_method
@@ -31,7 +32,7 @@ module Textbringer
31
32
  @@auto_detect_encodings = [
32
33
  Encoding::UTF_8,
33
34
  Encoding::EUC_JP,
34
- Encoding::Windows_31J
35
+ Encoding::WINDOWS_31J
35
36
  ]
36
37
 
37
38
  DEFAULT_DETECT_ENCODING = ->(s) {
@@ -225,6 +226,7 @@ module Textbringer
225
226
  file_encoding: CONFIG[:default_file_encoding],
226
227
  file_mtime: nil, new_file: true, undo_limit: UNDO_LIMIT,
227
228
  read_only: false)
229
+ @version = 0
228
230
  set_contents(s, file_encoding)
229
231
  @name = name
230
232
  @file_name = file_name
@@ -247,7 +249,7 @@ module Textbringer
247
249
  @undoing = false
248
250
  @composite_edit_level = 0
249
251
  @composite_edit_actions = []
250
- @version = 0
252
+ @file_version = 0
251
253
  @modified = false
252
254
  @mode = FundamentalMode.new(self)
253
255
  @minor_modes = []
@@ -454,7 +456,7 @@ module Textbringer
454
456
  if file_name != @file_name
455
457
  self.file_name = file_name
456
458
  end
457
- @version += 1
459
+ @file_version += 1
458
460
  @modified = false
459
461
  @new_file = false
460
462
  @read_only = false
@@ -605,6 +607,7 @@ module Textbringer
605
607
  end
606
608
  end
607
609
  @point = @gap_start += size
610
+ @version += 1
608
611
  update_line_and_column(pos, @point)
609
612
  unless @undoing
610
613
  if merge_undo && @undo_stack.last.is_a?(InsertAction)
@@ -645,6 +648,7 @@ module Textbringer
645
648
  s = @point
646
649
  pos = get_pos(@point, n)
647
650
  if n > 0
651
+ @version += 1
648
652
  str = substring(s, pos)
649
653
  # fill the gap with NUL to avoid invalid byte sequence in UTF-8
650
654
  @contents.bytesplice(@gap_end...user_to_gap(pos), "\0" * (pos - @point))
@@ -660,6 +664,7 @@ module Textbringer
660
664
  self.modified = true
661
665
  Utils.run_hooks(:after_change_functions, s, s, str) unless @undoing || !current?
662
666
  elsif n < 0
667
+ @version += 1
663
668
  str = substring(pos, s)
664
669
  update_line_and_column(@point, pos)
665
670
  # fill the gap with NUL to avoid invalid byte sequence in UTF-8
@@ -1014,6 +1019,7 @@ module Textbringer
1014
1019
  update_line_and_column(old_pos, s)
1015
1020
  save_point do
1016
1021
  str = substring(s, e)
1022
+ @version += 1
1017
1023
  @point = s
1018
1024
  adjust_gap
1019
1025
  len = e - s
@@ -1052,6 +1058,7 @@ module Textbringer
1052
1058
 
1053
1059
  def clear
1054
1060
  check_read_only_flag
1061
+ @version += 1
1055
1062
  @contents = String.new
1056
1063
  @point = @gap_start = @gap_end = 0
1057
1064
  @marks.each do |m|
@@ -1309,7 +1316,7 @@ module Textbringer
1309
1316
  @composite_edit_actions.each do |i|
1310
1317
  action.add_action(i)
1311
1318
  end
1312
- action.version = @composite_edit_actions.first.version
1319
+ action.file_version = @composite_edit_actions.first.file_version
1313
1320
  push_undo(action)
1314
1321
  @composite_edit_actions.clear
1315
1322
  end
@@ -1516,6 +1523,7 @@ module Textbringer
1516
1523
  private
1517
1524
 
1518
1525
  def set_contents(s, enc)
1526
+ @version += 1
1519
1527
  case s.encoding
1520
1528
  when Encoding::UTF_8, Encoding::ASCII_8BIT
1521
1529
  @contents = +s
@@ -1723,7 +1731,7 @@ module Textbringer
1723
1731
  def push_undo(action)
1724
1732
  return if @undoing || @undo_limit == 0
1725
1733
  if !modified?
1726
- action.version = @version
1734
+ action.file_version = @file_version
1727
1735
  end
1728
1736
  if @composite_edit_level > 0
1729
1737
  @composite_edit_actions.push(action)
@@ -1746,11 +1754,11 @@ module Textbringer
1746
1754
  begin
1747
1755
  was_modified = @modified
1748
1756
  action.send(op)
1749
- if action.version == @version
1757
+ if action.file_version == @file_version
1750
1758
  @modified = false
1751
- action.version = nil
1759
+ action.file_version = nil
1752
1760
  elsif !was_modified
1753
- action.version = @version
1761
+ action.file_version = @file_version
1754
1762
  end
1755
1763
  to_stack.push(action)
1756
1764
  ensure
@@ -1824,11 +1832,11 @@ module Textbringer
1824
1832
  KILL_RING = Ring.new
1825
1833
 
1826
1834
  class UndoableAction
1827
- attr_accessor :version
1835
+ attr_accessor :file_version
1828
1836
  attr_reader :location
1829
1837
 
1830
1838
  def initialize(buffer, location)
1831
- @version = nil
1839
+ @file_version = nil
1832
1840
  @buffer = buffer
1833
1841
  @location = location
1834
1842
  end
@@ -22,6 +22,30 @@ module Textbringer
22
22
  "brightwhite" => 15
23
23
  }
24
24
 
25
+ DIRECT_COLOR_THRESHOLD = 256 * 256 * 256
26
+
27
+ # Canonical RGB values for basic ANSI colors (xterm defaults),
28
+ # packed as 0xRRGGBB for use in direct color mode.
29
+ BASIC_COLORS_RGB = {
30
+ "default" => -1,
31
+ "black" => 0x000000,
32
+ "red" => 0xCD0000,
33
+ "green" => 0x00CD00,
34
+ "yellow" => 0xCDCD00,
35
+ "blue" => 0x0000EE,
36
+ "magenta" => 0xCD00CD,
37
+ "cyan" => 0x00CDCD,
38
+ "white" => 0xE5E5E5,
39
+ "brightblack" => 0x7F7F7F,
40
+ "brightred" => 0xFF0000,
41
+ "brightgreen" => 0x00FF00,
42
+ "brightyellow" => 0xFFFF00,
43
+ "brightblue" => 0x5C5CFF,
44
+ "brightmagenta" => 0xFF00FF,
45
+ "brightcyan" => 0x00FFFF,
46
+ "brightwhite" => 0xFFFFFF
47
+ }
48
+
25
49
  RGBColor = Struct.new(:r, :g, :b, :number)
26
50
 
27
51
  ADDITIONAL_COLORS = []
@@ -46,10 +70,17 @@ module Textbringer
46
70
  end
47
71
  end
48
72
 
73
+ def self.direct_color?
74
+ Window.colors >= DIRECT_COLOR_THRESHOLD
75
+ end
76
+
49
77
  def self.find_color_number(name)
50
78
  if name.is_a?(Integer)
51
79
  return name
52
80
  end
81
+ if direct_color?
82
+ return find_direct_color(name)
83
+ end
53
84
  case name
54
85
  when /\A\#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})\z/
55
86
  r = $1.to_i(16)
@@ -65,5 +96,20 @@ module Textbringer
65
96
  BASIC_COLORS[name]
66
97
  end
67
98
  end
99
+
100
+ def self.find_direct_color(name)
101
+ case name
102
+ when /\A\#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})\z/
103
+ r = $1.to_i(16)
104
+ g = $2.to_i(16)
105
+ b = $3.to_i(16)
106
+ (r << 16) | (g << 8) | b
107
+ else
108
+ unless BASIC_COLORS_RGB.key?(name)
109
+ raise EditorError, "No such color: #{name}"
110
+ end
111
+ BASIC_COLORS_RGB[name]
112
+ end
113
+ end
68
114
  end
69
115
  end
@@ -291,46 +291,48 @@ module Textbringer
291
291
  if CONFIG[:shell_file_name]
292
292
  cmd = [CONFIG[:shell_file_name], CONFIG[:shell_command_switch], cmd]
293
293
  end
294
- Open3.popen3(*cmd, opts) do |input, output, error, wait_thread|
295
- input.close
296
- catch(:finish) do
297
- loop do
298
- rs, = IO.select([output, error], nil, nil, 0.5)
299
- Window.redisplay
300
- rs&.each do |r|
301
- begin
302
- s = r.read_nonblock(1024).force_encoding("utf-8").
303
- scrub("\u{3013}").gsub(/\r\n/, "\n")
304
- buffer.insert(s)
305
- Window.redisplay
306
- rescue EOFError
307
- throw(:finish) if output.eof? && error.eof?
308
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK
309
- Window.redisplay
310
- next
294
+ with_clean_env do |env|
295
+ Open3.popen3(env, *cmd, opts) do |input, output, error, wait_thread|
296
+ input.close
297
+ catch(:finish) do
298
+ loop do
299
+ rs, = IO.select([output, error], nil, nil, 0.5)
300
+ Window.redisplay
301
+ rs&.each do |r|
302
+ begin
303
+ s = r.read_nonblock(1024).force_encoding("utf-8").
304
+ scrub("\u{3013}").gsub(/\r\n/, "\n")
305
+ buffer.insert(s)
306
+ Window.redisplay
307
+ rescue EOFError
308
+ throw(:finish) if output.eof? && error.eof?
309
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
310
+ Window.redisplay
311
+ next
312
+ end
311
313
  end
312
- end
313
- if received_keyboard_quit?
314
- if signals.empty?
315
- keyboard_quit
316
- else
317
- sig = signals.shift
318
- pid = wait_thread.pid
319
- pid = -pid if /mswin32|mingw32/ !~ RUBY_PLATFORM
320
- message("Send #{sig} to #{pid}")
321
- Process.kill(sig, pid)
314
+ if received_keyboard_quit?
315
+ if signals.empty?
316
+ keyboard_quit
317
+ else
318
+ sig = signals.shift
319
+ pid = wait_thread.pid
320
+ pid = -pid if /mswin32|mingw32/ !~ RUBY_PLATFORM
321
+ message("Send #{sig} to #{pid}")
322
+ Process.kill(sig, pid)
323
+ end
322
324
  end
323
325
  end
324
326
  end
325
- end
326
- status = wait_thread.value
327
- pid = status.pid
328
- if status.exited?
329
- code = status.exitstatus
330
- message("Process #{pid} exited with status code #{code}")
331
- elsif status.signaled?
332
- signame = Signal.signame(status.termsig)
333
- message("Process #{pid} was killed by #{signame}")
327
+ status = wait_thread.value
328
+ pid = status.pid
329
+ if status.exited?
330
+ code = status.exitstatus
331
+ message("Process #{pid} exited with status code #{code}")
332
+ elsif status.signaled?
333
+ signame = Signal.signame(status.termsig)
334
+ message("Process #{pid} was killed by #{signame}")
335
+ end
334
336
  end
335
337
  end
336
338
  ensure
@@ -376,5 +378,24 @@ module Textbringer
376
378
  describe_char
377
379
  end
378
380
  end
381
+ define_command(:load_theme,
382
+ doc: "Load and activate a theme by name.") do
383
+ |name = read_theme_name("Load theme: ")|
384
+ Theme.load(name)
385
+ Window.redisplay
386
+ message("Loaded theme: #{name}")
387
+ end
388
+
389
+ def read_theme_name(prompt)
390
+ builtin = Dir.glob(
391
+ File.expand_path("../../themes/*.rb", __FILE__)
392
+ ).map { |f| File.basename(f, ".rb") }
393
+ user = Dir.glob(
394
+ File.expand_path("~/.textbringer/themes/*.rb")
395
+ ).map { |f| File.basename(f, ".rb") }
396
+ names = (builtin + user).uniq.sort
397
+ f = ->(s) { complete_for_minibuffer(s, names) }
398
+ read_from_minibuffer(prompt, completion_proc: f)
399
+ end
379
400
  end
380
401
  end
@@ -84,6 +84,20 @@ module Textbringer
84
84
  end
85
85
  end
86
86
 
87
+ define_command(:set_foreground_color, doc: <<~EOD) do
88
+ Set the default foreground color.
89
+ EOD
90
+ |color = read_from_minibuffer("Foreground color: ")|
91
+ Window.set_default_colors(color, nil)
92
+ end
93
+
94
+ define_command(:set_background_color, doc: <<~EOD) do
95
+ Set the default background color.
96
+ EOD
97
+ |color = read_from_minibuffer("Background color: ")|
98
+ Window.set_default_colors(nil, color)
99
+ end
100
+
87
101
  define_command(:list_buffers, doc: <<~EOD) do |buffers = Buffer.list|
88
102
  List the existing buffers.
89
103
  EOD
@@ -18,6 +18,7 @@ module Textbringer
18
18
  ispell_command: "aspell -a",
19
19
  fill_column: 70,
20
20
  read_file_name_completion_ignore_case: RUBY_PLATFORM.match?(/darwin/),
21
- default_input_method: "t_code"
21
+ default_input_method: "t_code",
22
+ background_mode: nil
22
23
  }
23
24
  end
@@ -6,6 +6,7 @@ module Textbringer
6
6
 
7
7
  @@face_table = {}
8
8
  @@next_color_pair = 1
9
+ @@color_pair_cache = {}
9
10
 
10
11
  def self.[](name)
11
12
  @@face_table[name]
@@ -17,6 +18,8 @@ module Textbringer
17
18
  else
18
19
  @@face_table[name] = new(name, **opts)
19
20
  end
21
+ resolve_dependents(name)
22
+ @@face_table[name]
20
23
  end
21
24
 
22
25
  def self.delete(name)
@@ -25,26 +28,79 @@ module Textbringer
25
28
 
26
29
  def initialize(name, **opts)
27
30
  @name = name
28
- @color_pair = @@next_color_pair
29
- @@next_color_pair += 1
30
31
  update(**opts)
31
32
  end
32
33
 
33
- def update(foreground: -1, background: -1,
34
- bold: false, underline: false, reverse: false)
35
- @foreground = foreground
36
- @background = background
37
- @bold = bold
38
- @underline = underline
39
- @reverse = reverse
40
- Curses.init_pair(@color_pair,
41
- Color[foreground], Color[background])
34
+ UNSET = Object.new.freeze
35
+ private_constant :UNSET
36
+
37
+ def update(foreground: nil, background: nil,
38
+ bold: nil, underline: nil, reverse: nil,
39
+ inherit: UNSET)
40
+ unless inherit.equal?(UNSET)
41
+ if inherit && !inherit.is_a?(Symbol)
42
+ raise EditorError,
43
+ "Face inherit: must be a Symbol, got #{inherit.inspect}"
44
+ end
45
+ if inherit && cyclic_inheritance?(@name, inherit)
46
+ raise EditorError,
47
+ "Cyclic face inheritance: #{@name} inherits from #{inherit}"
48
+ end
49
+ @inherit = inherit
50
+ end
51
+ @explicit_foreground = foreground
52
+ @explicit_background = background
53
+ @explicit_bold = bold
54
+ @explicit_underline = underline
55
+ @explicit_reverse = reverse
56
+ resolve_inheritance
57
+ self
58
+ end
59
+
60
+ private
61
+
62
+ def resolve_inheritance
63
+ parent = @inherit ? self.class[@inherit] : nil
64
+ @foreground = @explicit_foreground || parent&.instance_variable_get(:@foreground) || -1
65
+ @background = @explicit_background || parent&.instance_variable_get(:@background) || -1
66
+ @bold = @explicit_bold.nil? ? (parent&.instance_variable_get(:@bold) || false) : @explicit_bold
67
+ @underline = @explicit_underline.nil? ? (parent&.instance_variable_get(:@underline) || false) : @explicit_underline
68
+ @reverse = @explicit_reverse.nil? ? (parent&.instance_variable_get(:@reverse) || false) : @explicit_reverse
69
+ fg_num = Color[@foreground]
70
+ bg_num = Color[@background]
71
+ key = [fg_num, bg_num]
72
+ unless @@color_pair_cache.key?(key)
73
+ @@color_pair_cache[key] = @@next_color_pair
74
+ Curses.init_pair(@@next_color_pair, fg_num, bg_num)
75
+ @@next_color_pair += 1
76
+ end
77
+ @color_pair = @@color_pair_cache[key]
42
78
  @text_attrs = 0
43
- @text_attrs |= Curses::A_BOLD if bold
44
- @text_attrs |= Curses::A_UNDERLINE if underline
45
- @text_attrs |= Curses::A_REVERSE if reverse
79
+ @text_attrs |= Curses::A_BOLD if @bold
80
+ @text_attrs |= Curses::A_UNDERLINE if @underline
81
+ @text_attrs |= Curses::A_REVERSE if @reverse
46
82
  @attributes = Curses.color_pair(@color_pair) | @text_attrs
47
- self
48
83
  end
84
+
85
+ def cyclic_inheritance?(name, inherit)
86
+ current = inherit
87
+ while current
88
+ return true if current == name
89
+ face = self.class[current]
90
+ current = face&.instance_variable_get(:@inherit)
91
+ end
92
+ false
93
+ end
94
+
95
+ def self.resolve_dependents(name, visited = {})
96
+ visited[name] = true
97
+ @@face_table.each_value do |face|
98
+ if face.instance_variable_get(:@inherit) == name
99
+ face.send(:resolve_inheritance)
100
+ resolve_dependents(face.name, visited) unless visited.key?(face.name)
101
+ end
102
+ end
103
+ end
104
+ private_class_method :resolve_dependents
49
105
  end
50
106
  end
@@ -63,9 +63,7 @@ module Textbringer
63
63
  }.join("\n")
64
64
  end
65
65
 
66
- def face_map
67
- highlight_on = {}
68
- highlight_off = {}
66
+ def apply_highlights(ctx)
69
67
  offset = 0
70
68
  @height.times do |y|
71
69
  offset += @margin_left
@@ -80,16 +78,14 @@ module Textbringer
80
78
  if face_name
81
79
  face = Face[face_name]
82
80
  if face
83
- highlight_on[offset] = face
84
81
  char_len = cell_char(value).bytesize
85
- highlight_off[offset + char_len] = true
82
+ ctx.highlight(offset, offset + char_len, face)
86
83
  end
87
84
  end
88
85
  offset += cell_char(value).bytesize
89
86
  end
90
87
  offset += 1 # newline
91
88
  end
92
- [highlight_on, highlight_off]
93
89
  end
94
90
 
95
91
  # Timer
@@ -0,0 +1,21 @@
1
+ module Textbringer
2
+ class HighlightContext
3
+ attr_reader :buffer, :highlight_start, :highlight_end
4
+
5
+ def initialize(buffer:, highlight_start:, highlight_end:,
6
+ highlight_on:, highlight_off:)
7
+ @buffer = buffer
8
+ @highlight_start = highlight_start
9
+ @highlight_end = highlight_end
10
+ @highlight_on = highlight_on
11
+ @highlight_off = highlight_off
12
+ end
13
+
14
+ def highlight(start_offset, end_offset, face)
15
+ start_offset = @highlight_start if start_offset < @highlight_start &&
16
+ @highlight_start < end_offset
17
+ @highlight_on[start_offset] = face
18
+ @highlight_off[end_offset] = true
19
+ end
20
+ end
21
+ end
@@ -307,7 +307,7 @@ module Textbringer
307
307
  when "x"
308
308
  prev_candidate
309
309
  nil
310
- when "\C-m", "\r", "\n"
310
+ when "\r", "\n"
311
311
  confirm_selecting
312
312
  nil
313
313
  else
@@ -81,5 +81,30 @@ module Textbringer
81
81
  def syntax_table
82
82
  self.class.syntax_table
83
83
  end
84
+
85
+ def highlight(ctx)
86
+ syntax_table = self.class.syntax_table || DEFAULT_SYNTAX_TABLE
87
+ if ctx.buffer.bytesize < CONFIG[:highlight_buffer_size_limit]
88
+ base_pos = ctx.buffer.point_min
89
+ s = ctx.buffer.to_s
90
+ else
91
+ base_pos = ctx.highlight_start
92
+ s = ctx.buffer.substring(ctx.highlight_start,
93
+ ctx.highlight_end).scrub("")
94
+ end
95
+ return if !s.valid_encoding?
96
+ re_str = syntax_table.map { |name, re|
97
+ "(?<#{name}>#{re})"
98
+ }.join("|")
99
+ re = Regexp.new(re_str)
100
+ names = syntax_table.keys
101
+ s.scan(re) do
102
+ b = base_pos + $`.bytesize
103
+ e = b + $&.bytesize
104
+ name = names.find { |n| $~[n] }
105
+ face = Face[name]
106
+ ctx.highlight(b, e, face) if face
107
+ end
108
+ end
84
109
  end
85
110
  end
@@ -188,7 +188,6 @@ module Textbringer
188
188
  doc: "Delete files flagged for deletion.") do
189
189
  files = collect_flagged_files
190
190
  return if files.empty?
191
- list = files.map { |f| " #{f}" }.join("\n")
192
191
  if yes_or_no?("Delete these files?")
193
192
  files.each do |name|
194
193
  next if name == "." || name == ".."
@@ -21,10 +21,15 @@ module Textbringer
21
21
  grid = Gamegrid.new(width, height, margin_left: margin_left)
22
22
  @buffer[:gamegrid] = grid
23
23
  @buffer.read_only = true
24
- @buffer[:highlight_override] = -> { grid.face_map }
25
24
  grid
26
25
  end
27
26
 
27
+ def highlight(ctx)
28
+ grid = @buffer[:gamegrid]
29
+ return unless grid
30
+ grid.apply_highlights(ctx)
31
+ end
32
+
28
33
  define_local_command(:gamegrid_refresh,
29
34
  doc: "Refresh the gamegrid display.") do
30
35
  grid = @buffer[:gamegrid]