textbringer 22 → 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 +4 -4
- data/CLAUDE.md +0 -6
- data/lib/textbringer/buffer.rb +2 -0
- data/lib/textbringer/input_method.rb +6 -0
- data/lib/textbringer/input_methods/skk_input_method.rb +105 -20
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0fe4d897c03de987989c81559c4c7d354f9d2afd9ec8cc7ff819c5da0a2c1bb4
|
|
4
|
+
data.tar.gz: c687cc3726ea8d9ccfd9efcb235590f9d4d447145cb9a0a6eaeb103b133f671f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
```
|
data/lib/textbringer/buffer.rb
CHANGED
|
@@ -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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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(
|
|
461
|
-
@okuri_roman =
|
|
462
|
-
|
|
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
|
-
|
|
504
|
-
|
|
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"
|
data/lib/textbringer/version.rb
CHANGED
data/lib/textbringer/window.rb
CHANGED