textbringer 21 → 23

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: 9fb85ae8a3790d1ae876ecac31730522e121047dde8cf759bfb58e137ac5fb59
4
- data.tar.gz: 70c9b7d737cda7b8fcc2bbfab347c2d22a47743ca9ec5d3c6863c4bee8b5005d
3
+ metadata.gz: 0fe4d897c03de987989c81559c4c7d354f9d2afd9ec8cc7ff819c5da0a2c1bb4
4
+ data.tar.gz: c687cc3726ea8d9ccfd9efcb235590f9d4d447145cb9a0a6eaeb103b133f671f
5
5
  SHA512:
6
- metadata.gz: 87cecff29b608feff2999cda9619b429d4158b74e6e2ef3d5560f2b742170553b5101996a6907ff1d9c8852086e76b429166108586f353ac73a1d0354276f4df
7
- data.tar.gz: 4208176e8fb0ce43237786129ddbcd5a77b8b052b7003ad414c7b018b35086c5f68dfa944c3ade415969f18484e9e298a980ef5e138668fbd60286cc697456a5
6
+ metadata.gz: c1885974de9ae18238d0a49f436e3300d5971c06b7d3b16fb9c21cfc36c42b15e1beb22ee4608a80252100e202cef01d0943fb8d68a3db6ec9884773aff02093
7
+ data.tar.gz: 75bdc11e89da85249a54568e8d23ecd0979934143604918b292c01f4630a59386fd2965c7a260d9fc9b05c8a5f81695ecf23e9306fcb5df04535f97d65a927b7
data/CLAUDE.md CHANGED
@@ -35,12 +35,6 @@ txtb
35
35
  # Run all tests
36
36
  bundle exec rake test
37
37
 
38
- # Or simply (default task)
39
- bundle exec rake
40
-
41
- # On Ubuntu/Linux (for CI)
42
- xvfb-run bundle exec rake test
43
-
44
38
  # Run a single test file
45
39
  ruby -Ilib:test test/textbringer/test_buffer.rb
46
40
  ```
@@ -112,11 +112,13 @@ module Textbringer
112
112
  end
113
113
 
114
114
  def self.current=(buffer)
115
+ @@current&.input_method&.on_deactivate
115
116
  if buffer && buffer.name && @@table.key?(buffer.name)
116
117
  @@list.delete(buffer)
117
118
  @@list.unshift(buffer)
118
119
  end
119
120
  @@current = buffer
121
+ @@current&.input_method&.on_activate
120
122
  end
121
123
 
122
124
  def self.minibuffer
@@ -2,7 +2,7 @@ require "curses"
2
2
 
3
3
  module Textbringer
4
4
  class Face
5
- attr_reader :name, :attributes
5
+ attr_reader :name, :attributes, :color_pair, :text_attrs
6
6
 
7
7
  @@face_table = {}
8
8
  @@next_color_pair = 1
@@ -39,11 +39,11 @@ module Textbringer
39
39
  @reverse = reverse
40
40
  Curses.init_pair(@color_pair,
41
41
  Color[foreground], Color[background])
42
- @attributes = 0
43
- @attributes |= Curses.color_pair(@color_pair)
44
- @attributes |= Curses::A_BOLD if bold
45
- @attributes |= Curses::A_UNDERLINE if underline
46
- @attributes |= Curses::A_REVERSE if reverse
42
+ @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
46
+ @attributes = Curses.color_pair(@color_pair) | @text_attrs
47
47
  self
48
48
  end
49
49
  end
@@ -142,24 +142,13 @@ module Textbringer
142
142
  @buffer.save_point do |point|
143
143
  @window.erase
144
144
 
145
- # Get face attributes if face is specified
146
- face_attrs = 0
147
- if @face && Window.has_colors?
148
- face = Face[@face]
149
- face_attrs = face.attributes if face
150
- end
145
+ # Get face if face is specified
146
+ face = @face && Window.has_colors? ? Face[@face] : nil
151
147
 
152
- # Get current line face attributes if specified
153
- current_line_attrs = 0
154
- if @current_line_face && Window.has_colors?
155
- current_line_face = Face[@current_line_face]
156
- current_line_attrs = current_line_face.attributes if current_line_face
157
- end
148
+ # Get current line face if specified
149
+ current_line_face = @current_line_face && Window.has_colors? ? Face[@current_line_face] : nil
158
150
 
159
- @window.attrset(face_attrs)
160
- @in_region = false
161
- @in_isearch = false
162
- @current_highlight_attrs = face_attrs
151
+ apply_face_attrs(@window, face)
163
152
 
164
153
  # First pass: find which line contains point
165
154
  point_line = nil
@@ -195,11 +184,10 @@ module Textbringer
195
184
  @window.setpos(line_num, 0)
196
185
 
197
186
  # Determine which face to use for this line
198
- line_attrs = if @current_line_face && line_num == point_line
199
- current_line_attrs
200
- else
201
- face_attrs
202
- end
187
+ line_face = @current_line_face && line_num == point_line ? current_line_face : face
188
+
189
+ # Set attributes for the entire line
190
+ apply_face_attrs(@window, line_face)
203
191
 
204
192
  # Render characters on this line
205
193
  col = 0
@@ -228,29 +216,17 @@ module Textbringer
228
216
  break
229
217
  end
230
218
 
231
- # Apply face attributes to all characters
232
- if line_attrs != 0
233
- @window.attron(line_attrs)
234
- end
235
219
  @window.addstr(s)
236
- if line_attrs != 0
237
- @window.attroff(line_attrs)
238
- end
239
220
 
240
221
  col += char_width
241
222
  @buffer.forward_char
242
223
  end
243
224
 
244
225
  # Fill remaining space on the line with the face background
245
- if line_attrs != 0 && col < @columns
246
- @window.attron(line_attrs)
247
- @window.addstr(" " * (@columns - col))
248
- @window.attroff(line_attrs)
249
- elsif line_attrs == 0 && face_attrs != 0 && col < @columns
250
- # Use default face for padding if no line-specific attrs
251
- @window.attron(face_attrs)
252
- @window.addstr(" " * (@columns - col))
253
- @window.attroff(face_attrs)
226
+ if col < @columns
227
+ fill_face = line_face || face
228
+ apply_face_attrs(@window, fill_face)
229
+ @window.addstr(" " * (@columns - col)) if fill_face
254
230
  end
255
231
 
256
232
  # Track cursor position
@@ -263,12 +239,11 @@ module Textbringer
263
239
  end
264
240
 
265
241
  # Fill remaining lines with the face background
266
- if face_attrs != 0
242
+ if face
267
243
  while line_num < @lines
268
244
  @window.setpos(line_num, 0)
269
- @window.attron(face_attrs)
245
+ apply_face_attrs(@window, face)
270
246
  @window.addstr(" " * @columns)
271
- @window.attroff(face_attrs)
272
247
  line_num += 1
273
248
  end
274
249
  end
@@ -36,6 +36,12 @@ module Textbringer
36
36
  @enabled = false
37
37
  end
38
38
 
39
+ def on_activate
40
+ end
41
+
42
+ def on_deactivate
43
+ end
44
+
39
45
  def enabled?
40
46
  @enabled
41
47
  end
@@ -1,8 +1,12 @@
1
1
  require "open-uri"
2
2
  require "fileutils"
3
+ require "socket"
4
+ require "timeout"
3
5
 
4
6
  module Textbringer
5
7
  CONFIG[:skk_dictionary_path] = File.expand_path("~/.textbringer/skk/SKK-JISYO.L")
8
+ CONFIG[:skk_server_host] = nil # nil = disabled (default)
9
+ CONFIG[:skk_server_port] = 1178
6
10
 
7
11
  class SkkInputMethod < InputMethod
8
12
  HIRAGANA_TABLE = {
@@ -46,7 +50,11 @@ module Textbringer
46
50
  "xtu" => "っ", "xtsu" => "っ",
47
51
  "xya" => "ゃ", "xyu" => "ゅ", "xyo" => "ょ",
48
52
  "xa" => "ぁ", "xi" => "ぃ", "xu" => "ぅ", "xe" => "ぇ", "xo" => "ぉ",
49
- "," => "、", "." => "。",
53
+ "-" => "ー", "," => "、", "." => "。", "[" => "「", "]" => "」",
54
+ "z-" => "~", "z." => "…", "z/" => "・", "z," => "‥",
55
+ "z(" => "(", "z)" => ")", "z[" => "『", "z]" => "』",
56
+ "zh" => "←", "zj" => "↓", "zk" => "↑", "zl" => "→", "zL" => "⇒",
57
+ "z " => " ",
50
58
  }
51
59
 
52
60
  HIRAGANA_PREFIXES = HIRAGANA_TABLE.keys.flat_map { |s|
@@ -99,6 +107,14 @@ module Textbringer
99
107
  (s.size - 1).times.map { |i| s[0, i + 1] }
100
108
  }.uniq
101
109
 
110
+ STATUS_NAMES = {
111
+ hiragana: "かな",
112
+ katakana: "カナ",
113
+ hankaku_katakana: "半カナ",
114
+ zenkaku_ascii: "全英",
115
+ ascii: "SKK:"
116
+ }
117
+
102
118
  DEFAULT_CURSOR_COLORS = {
103
119
  hiragana: "pink",
104
120
  katakana: "green",
@@ -120,6 +136,7 @@ module Textbringer
120
136
  @marker_pos = nil
121
137
  @okuriiari = nil
122
138
  @okurinasi = nil
139
+ @skk_server_socket = nil
123
140
  end
124
141
 
125
142
  def toggle
@@ -128,22 +145,26 @@ module Textbringer
128
145
  update_cursor_color
129
146
  else
130
147
  reset_cursor_color
148
+ close_skk_server
131
149
  end
132
150
  end
133
151
 
134
152
  def disable
135
153
  super
136
154
  reset_cursor_color
155
+ close_skk_server
156
+ end
157
+
158
+ def on_activate
159
+ update_cursor_color if @enabled
160
+ end
161
+
162
+ def on_deactivate
163
+ reset_cursor_color if @enabled
137
164
  end
138
165
 
139
166
  def status
140
- case @phase
141
- when :converting then "▽"
142
- when :selecting then "▼"
143
- else
144
- { hiragana: "あ", katakana: "ア", hankaku_katakana: "ア",
145
- zenkaku_ascii: "A", ascii: "A" }[@mode]
146
- end
167
+ STATUS_NAMES[@mode]
147
168
  end
148
169
 
149
170
  def handle_event(event)
@@ -201,7 +222,7 @@ module Textbringer
201
222
  process_romaji(event)
202
223
  end
203
224
  when "l"
204
- if [:hiragana, :katakana, :hankaku_katakana].include?(@mode)
225
+ if @roman_buffer != "z" && [:hiragana, :katakana, :hankaku_katakana].include?(@mode)
205
226
  @roman_buffer = +""
206
227
  @mode = :ascii
207
228
  Window.redisplay
@@ -211,7 +232,7 @@ module Textbringer
211
232
  process_romaji(event)
212
233
  end
213
234
  when "L"
214
- if [:hiragana, :katakana, :hankaku_katakana].include?(@mode)
235
+ if @roman_buffer != "z" && [:hiragana, :katakana, :hankaku_katakana].include?(@mode)
215
236
  @roman_buffer = +""
216
237
  @mode = :zenkaku_ascii
217
238
  Window.redisplay
@@ -389,8 +410,8 @@ module Textbringer
389
410
  if kana
390
411
  @roman_buffer = +""
391
412
  if @okuri_roman
392
- # Completing okurigana
393
- @okuri_kana = kana
413
+ # Completing okurigana (accumulate in case a vowel kana was already prepended)
414
+ @okuri_kana = (@okuri_kana || "") + kana
394
415
  with_target_buffer do |buffer|
395
416
  buffer.insert(kana)
396
417
  end
@@ -429,7 +450,14 @@ module Textbringer
429
450
  first_char = @roman_buffer[0]
430
451
  last_char = @roman_buffer[-1]
431
452
  @roman_buffer = +""
432
- append_yomi_kana(first_char)
453
+ if @okuri_roman && (kana = HIRAGANA_TABLE[first_char])
454
+ # Vowel starts okurigana: accumulate kana and continue buffering the rest
455
+ @okuri_kana = (@okuri_kana || "") + kana
456
+ with_target_buffer { |b| b.insert(kana) }
457
+ Window.redisplay
458
+ else
459
+ append_yomi_kana(first_char)
460
+ end
433
461
  process_converting_romaji(last_char)
434
462
  end
435
463
 
@@ -457,9 +485,20 @@ module Textbringer
457
485
  nil
458
486
  end
459
487
 
460
- def start_okurigana(consonant)
461
- @okuri_roman = consonant.dup
462
- @roman_buffer = consonant.dup
488
+ def start_okurigana(c)
489
+ @okuri_roman = c.dup
490
+ kana = HIRAGANA_TABLE[c]
491
+ if kana
492
+ # Vowel okurigana: insert the kana immediately and record it in @okuri_kana.
493
+ # (A vowel is never a prefix of a longer romaji sequence, so it's always complete.)
494
+ @okuri_kana = kana
495
+ @roman_buffer = +""
496
+ with_target_buffer { |b| b.insert(kana) }
497
+ Window.redisplay
498
+ start_selecting
499
+ else
500
+ @roman_buffer = c.dup
501
+ end
463
502
  end
464
503
 
465
504
  def cancel_converting
@@ -492,16 +531,19 @@ module Textbringer
492
531
  end
493
532
 
494
533
  def start_selecting
495
- ensure_dictionary_loaded
496
-
497
534
  lookup_key = if @okuri_roman
498
535
  @yomi + @okuri_roman
499
536
  else
500
537
  @yomi
501
538
  end
502
539
 
503
- dict = @okuri_roman ? @okuriiari : @okurinasi
504
- candidates = dict[lookup_key]
540
+ candidates = if CONFIG[:skk_server_host]
541
+ skk_server_lookup(lookup_key)
542
+ else
543
+ ensure_dictionary_loaded
544
+ dict = @okuri_roman ? @okuriiari : @okurinasi
545
+ dict[lookup_key]
546
+ end
505
547
 
506
548
  if candidates.nil? || candidates.empty?
507
549
  message("No conversion: #{@yomi}")
@@ -750,6 +792,49 @@ module Textbringer
750
792
  STDOUT.write("\e]112\a")
751
793
  STDOUT.flush
752
794
  end
795
+
796
+ SKK_SERVER_TIMEOUT = 3 # seconds
797
+
798
+ def close_skk_server
799
+ return unless @skk_server_socket
800
+ begin
801
+ @skk_server_socket.write("0\n")
802
+ rescue IOError, Errno::EPIPE
803
+ end
804
+ @skk_server_socket.close rescue nil
805
+ @skk_server_socket = nil
806
+ end
807
+
808
+ def skk_server_connect
809
+ return true if @skk_server_socket && !@skk_server_socket.closed?
810
+ host = CONFIG[:skk_server_host]
811
+ return false unless host
812
+ port = CONFIG[:skk_server_port] || 1178
813
+ Timeout.timeout(SKK_SERVER_TIMEOUT) do
814
+ @skk_server_socket = TCPSocket.new(host, port)
815
+ end
816
+ true
817
+ end
818
+
819
+ def skk_server_lookup(lookup_key)
820
+ skk_server_connect
821
+ begin
822
+ Timeout.timeout(SKK_SERVER_TIMEOUT) do
823
+ @skk_server_socket.write("1#{lookup_key} \n".encode("EUC-JP"))
824
+ response = @skk_server_socket.gets("\n")
825
+ return nil unless response
826
+ response = response.encode("UTF-8", "EUC-JP", invalid: :replace, undef: :replace).chomp
827
+ return nil unless response.start_with?("1/")
828
+ body = response[2..]
829
+ candidates = body.split("/").map { |c| c.split(";").first&.strip }.compact.reject(&:empty?)
830
+ candidates.empty? ? nil : candidates
831
+ end
832
+ rescue Timeout::Error, IOError, Errno::EPIPE, Errno::ECONNRESET
833
+ @skk_server_socket.close rescue nil
834
+ @skk_server_socket = nil
835
+ nil
836
+ end
837
+ end
753
838
  end
754
839
 
755
840
  SKK_DICTIONARY_URL = "https://github.com/skk-dev/dict/raw/090619ac57ef230a0506c191b569fc8c82b1025b/SKK-JISYO.L"
@@ -18,8 +18,8 @@ module Textbringer
18
18
  :backward_char,
19
19
  :forward_word,
20
20
  :backward_word,
21
- :scroll_up_command,
22
- :scroll_down_command,
21
+ :scroll_up,
22
+ :scroll_down,
23
23
  :beginning_of_buffer,
24
24
  :end_of_buffer,
25
25
  # Search commands
@@ -1,3 +1,3 @@
1
1
  module Textbringer
2
- VERSION = "21"
2
+ VERSION = "23"
3
3
  end
@@ -1,6 +1,16 @@
1
1
  require "curses"
2
2
  require_relative "window/fallback_characters"
3
3
 
4
+ unless Curses::Window.method_defined?(:attr_set)
5
+ using Module.new {
6
+ refine Curses::Window do
7
+ def attr_set(attrs, pair)
8
+ attrset(attrs | Curses.color_pair(pair))
9
+ end
10
+ end
11
+ }
12
+ end
13
+
4
14
  module Textbringer
5
15
  class Window
6
16
  Cursor = Struct.new(:y, :x)
@@ -146,6 +156,10 @@ module Textbringer
146
156
  Curses.start_color
147
157
  Curses.use_default_colors
148
158
  load_faces
159
+ else
160
+ Face.define :mode_line, reverse: true
161
+ Face.define :region, reverse: true
162
+ Face.define :isearch, underline: true
149
163
  end
150
164
  begin
151
165
  window =
@@ -161,6 +175,7 @@ module Textbringer
161
175
  @@started = true
162
176
  yield
163
177
  ensure
178
+ Buffer.current&.input_method&.on_deactivate
164
179
  @@list.each do |win|
165
180
  win.close
166
181
  end
@@ -261,7 +276,7 @@ module Textbringer
261
276
  @cursor = Cursor.new(0, 0)
262
277
  @in_region = false
263
278
  @in_isearch = false
264
- @current_highlight_attrs = 0
279
+ @current_hl_face = nil
265
280
  end
266
281
 
267
282
  def echo_area?
@@ -410,10 +425,10 @@ module Textbringer
410
425
  b = @buffer.point
411
426
  end
412
427
  name = names.find { |n| $~[n] }
413
- attributes = Face[name]&.attributes
414
- if attributes
415
- @highlight_on[b] = attributes
416
- @highlight_off[e] = attributes
428
+ face = Face[name]
429
+ if face
430
+ @highlight_on[b] = face
431
+ @highlight_off[e] = true
417
432
  end
418
433
  end
419
434
  end
@@ -434,45 +449,30 @@ module Textbringer
434
449
  highlight
435
450
  @window.erase
436
451
  @window.setpos(0, 0)
437
- @window.attrset(0)
438
452
  @in_region = false
439
453
  @in_isearch = false
440
- @current_highlight_attrs = 0
454
+ @current_hl_face = nil
441
455
  if current_or_minibuffer_selected? && @buffer.visible_mark &&
442
456
  @buffer.point_after_mark?(@buffer.visible_mark)
443
- @window.attron(region_attr)
444
457
  @in_region = true
445
458
  end
446
459
  if current_or_minibuffer_selected? && @buffer.isearch_mark &&
447
460
  @buffer.point_after_mark?(@buffer.isearch_mark)
448
- # If already in region, switch to isearch (priority)
449
- if @in_region
450
- @window.attroff(region_attr)
451
- end
452
- @window.attron(isearch_attr)
461
+ @in_region = false
453
462
  @in_isearch = true
454
463
  end
464
+ apply_window_attrs(@window)
455
465
  while !@buffer.end_of_buffer?
456
466
  cury = @window.cury
457
467
  curx = @window.curx
458
468
  update_cursor_and_attr(point, cury, curx)
459
- if attrs = @highlight_off[@buffer.point]
460
- if @in_region || @in_isearch
461
- # In region: only turn off non-color attributes (bold, underline, etc.)
462
- @window.attroff(attrs & ~Curses::A_COLOR)
463
- else
464
- @window.attroff(attrs)
465
- end
466
- @current_highlight_attrs = 0
469
+ if @highlight_off[@buffer.point]
470
+ @current_hl_face = nil
471
+ apply_window_attrs(@window)
467
472
  end
468
- if attrs = @highlight_on[@buffer.point]
469
- if @in_region || @in_isearch
470
- # In region: only turn on non-color attributes (preserve region background)
471
- @window.attron(attrs & ~Curses::A_COLOR)
472
- else
473
- @window.attron(attrs)
474
- end
475
- @current_highlight_attrs = attrs
473
+ if face = @highlight_on[@buffer.point]
474
+ @current_hl_face = face
475
+ apply_window_attrs(@window)
476
476
  end
477
477
  c = @buffer.char_after
478
478
  if c == "\n"
@@ -517,12 +517,7 @@ module Textbringer
517
517
  break if newx == columns && cury == lines - 2
518
518
  @buffer.forward_char
519
519
  end
520
- if current_or_minibuffer_selected? && @buffer.isearch_mark
521
- @window.attroff(isearch_attr)
522
- end
523
- if current_or_minibuffer_selected? && @buffer.visible_mark
524
- @window.attroff(region_attr)
525
- end
520
+ apply_window_attrs(@window)
526
521
  @buffer.mark_to_point(@bottom_of_window)
527
522
  if @buffer.point_at_mark?(point)
528
523
  @cursor.y = @window.cury
@@ -712,8 +707,8 @@ module Textbringer
712
707
  def redisplay_mode_line
713
708
  @mode_line.erase
714
709
  @mode_line.setpos(0, 0)
715
- attrs = @@has_colors ? Face[:mode_line].attributes : Curses::A_REVERSE
716
- @mode_line.attrset(attrs)
710
+ face = Face[:mode_line]
711
+ @mode_line.attr_set(face.text_attrs, face.color_pair)
717
712
  @mode_line.addstr("#{@buffer.input_method_status} #{@buffer.name} ")
718
713
  @mode_line.addstr("[+]") if @buffer.modified?
719
714
  @mode_line.addstr("[RO]") if @buffer.read_only?
@@ -731,7 +726,7 @@ module Textbringer
731
726
  @mode_line.addstr(" #{line},#{column}")
732
727
  @mode_line.addstr(" (#{@buffer.mode_names.join(' ')})")
733
728
  @mode_line.addstr(" " * (columns - @mode_line.curx))
734
- @mode_line.attrset(0)
729
+ @mode_line.attr_set(0, 0)
735
730
  @mode_line.noutrefresh
736
731
  end
737
732
 
@@ -746,45 +741,28 @@ module Textbringer
746
741
  end
747
742
 
748
743
  def update_cursor_and_attr(point, cury, curx)
744
+ changed = false
749
745
  if @buffer.point_at_mark?(point)
750
746
  @cursor.y = cury
751
747
  @cursor.x = curx
752
748
  # Handle visible mark transitions
753
749
  if current_or_minibuffer_selected? && @buffer.visible_mark
754
750
  if @buffer.point_after_mark?(@buffer.visible_mark)
755
- unless @in_isearch
756
- @window.attroff(region_attr)
757
- # Restore syntax highlighting colors after exiting region
758
- if @current_highlight_attrs != 0
759
- @window.attron(@current_highlight_attrs)
760
- end
761
- end
762
751
  @in_region = false
752
+ changed = true
763
753
  elsif @buffer.point_before_mark?(@buffer.visible_mark)
764
- unless @in_isearch
765
- @window.attron(region_attr)
766
- end
767
754
  @in_region = true
755
+ changed = true
768
756
  end
769
757
  end
770
758
  # Handle isearch mark transitions
771
759
  if current_or_minibuffer_selected? && @buffer.isearch_mark
772
760
  if @buffer.point_after_mark?(@buffer.isearch_mark)
773
- @window.attroff(isearch_attr)
774
761
  @in_isearch = false
775
- # If we were covering a region, restore it
776
- if @in_region
777
- @window.attron(region_attr)
778
- elsif @current_highlight_attrs != 0
779
- @window.attron(@current_highlight_attrs)
780
- end
762
+ changed = true
781
763
  elsif @buffer.point_before_mark?(@buffer.isearch_mark)
782
- # Entering isearch - turn off region if active
783
- if @in_region
784
- @window.attroff(region_attr)
785
- end
786
- @window.attron(isearch_attr)
787
764
  @in_isearch = true
765
+ changed = true
788
766
  end
789
767
  end
790
768
  end
@@ -792,42 +770,25 @@ module Textbringer
792
770
  if current_or_minibuffer_selected? && @buffer.isearch_mark &&
793
771
  @buffer.point_at_mark?(@buffer.isearch_mark)
794
772
  if @buffer.point_after_mark?(point)
795
- @window.attroff(isearch_attr)
796
773
  @in_isearch = false
797
- # If we have a region underneath, restore it
798
- if @in_region
799
- @window.attron(region_attr)
800
- elsif @current_highlight_attrs != 0
801
- @window.attron(@current_highlight_attrs)
802
- end
774
+ changed = true
803
775
  elsif @buffer.point_before_mark?(point)
804
- # Entering isearch - turn off region if active
805
- if @in_region
806
- @window.attroff(region_attr)
807
- end
808
- @window.attron(isearch_attr)
809
776
  @in_isearch = true
777
+ changed = true
810
778
  end
811
779
  end
812
780
  # Handle transitions when point crosses visible mark
813
781
  if current_or_minibuffer_selected? && @buffer.visible_mark &&
814
782
  @buffer.point_at_mark?(@buffer.visible_mark)
815
783
  if @buffer.point_after_mark?(point)
816
- unless @in_isearch
817
- @window.attroff(region_attr)
818
- # Restore syntax highlighting colors after exiting region
819
- if @current_highlight_attrs != 0
820
- @window.attron(@current_highlight_attrs)
821
- end
822
- end
823
784
  @in_region = false
785
+ changed = true
824
786
  elsif @buffer.point_before_mark?(point)
825
- unless @in_isearch
826
- @window.attron(region_attr)
827
- end
828
787
  @in_region = true
788
+ changed = true
829
789
  end
830
790
  end
791
+ apply_window_attrs(@window) if changed
831
792
  end
832
793
 
833
794
  def compose_character(point, cury, curx, c)
@@ -1004,12 +965,28 @@ module Textbringer
1004
965
  end
1005
966
  end
1006
967
 
1007
- def region_attr
1008
- @@has_colors ? Face[:region].attributes : Curses::A_REVERSE
968
+ def apply_face_attrs(win, face)
969
+ win.attr_set(face&.text_attrs || 0, face&.color_pair || 0)
1009
970
  end
1010
971
 
1011
- def isearch_attr
1012
- @@has_colors ? Face[:isearch].attributes : Curses::A_UNDERLINE
972
+ def apply_window_attrs(win)
973
+ if @in_isearch
974
+ face = Face[:isearch]
975
+ elsif @in_region
976
+ face = Face[:region]
977
+ else
978
+ face = @current_hl_face
979
+ end
980
+
981
+ if face
982
+ text_attrs = face.text_attrs
983
+ if (@in_isearch || @in_region) && @current_hl_face
984
+ text_attrs |= @current_hl_face.text_attrs
985
+ end
986
+ win.attr_set(text_attrs, face.color_pair)
987
+ else
988
+ win.attr_set(0, 0)
989
+ end
1013
990
  end
1014
991
  end
1015
992
 
@@ -1057,10 +1034,10 @@ module Textbringer
1057
1034
  @window.addstr(@buffer.input_method_status)
1058
1035
  end
1059
1036
  @window.setpos(0, 0)
1060
- @window.attrset(0)
1037
+ @window.attr_set(0, 0)
1061
1038
  @in_region = false
1062
1039
  @in_isearch = false
1063
- @current_highlight_attrs = 0
1040
+ @current_hl_face = nil
1064
1041
  if @message
1065
1042
  @window.addstr(escape(@message))
1066
1043
  else
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textbringer
3
3
  version: !ruby/object:Gem::Version
4
- version: '21'
4
+ version: '23'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda