textbringer 1.0.1 → 1.1.0
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/.github/workflows/ubuntu.yml +5 -9
- data/.github/workflows/windows.yml +4 -7
- data/CHANGES.md +47 -0
- data/README.md +0 -2
- data/bin/merge_mazegaki_dic +25 -0
- data/exe/textbringer +6 -5
- data/lib/textbringer.rb +4 -0
- data/lib/textbringer/buffer.rb +40 -3
- data/lib/textbringer/commands/clipboard.rb +3 -3
- data/lib/textbringer/commands/ctags.rb +3 -3
- data/lib/textbringer/commands/dabbrev.rb +1 -1
- data/lib/textbringer/commands/files.rb +18 -3
- data/lib/textbringer/commands/help.rb +25 -16
- data/lib/textbringer/commands/input_method.rb +18 -0
- data/lib/textbringer/commands/isearch.rb +23 -2
- data/lib/textbringer/commands/misc.rb +8 -3
- data/lib/textbringer/commands/windows.rb +8 -4
- data/lib/textbringer/config.rb +2 -1
- data/lib/textbringer/controller.rb +7 -1
- data/lib/textbringer/input_method.rb +63 -0
- data/lib/textbringer/input_methods/hiragana_input_method.rb +70 -0
- data/lib/textbringer/input_methods/t_code_input_method.rb +458 -0
- data/lib/textbringer/input_methods/t_code_input_method/tables.rb +64 -0
- data/lib/textbringer/keymap.rb +45 -26
- data/lib/textbringer/modes/backtrace_mode.rb +1 -1
- data/lib/textbringer/modes/buffer_list_mode.rb +1 -1
- data/lib/textbringer/modes/c_mode.rb +4 -0
- data/lib/textbringer/modes/completion_list_mode.rb +1 -1
- data/lib/textbringer/modes/help_mode.rb +1 -1
- data/lib/textbringer/modes/programming_mode.rb +24 -2
- data/lib/textbringer/modes/ruby_mode.rb +70 -12
- data/lib/textbringer/utils.rb +10 -5
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +25 -10
- data/textbringer.gemspec +4 -5
- metadata +19 -28
- data/.github/workflows/ruby-head.yml +0 -30
@@ -1,6 +1,6 @@
|
|
1
1
|
module Textbringer
|
2
2
|
module Commands
|
3
|
-
ISEARCH_MODE_MAP
|
3
|
+
define_keymap :ISEARCH_MODE_MAP
|
4
4
|
(?\x20..?\x7e).each do |c|
|
5
5
|
ISEARCH_MODE_MAP.define_key(c, :isearch_printing_char)
|
6
6
|
end
|
@@ -14,11 +14,14 @@ module Commands
|
|
14
14
|
end
|
15
15
|
ISEARCH_MODE_MAP.define_key(:backspace, :isearch_delete_char)
|
16
16
|
ISEARCH_MODE_MAP.define_key(?\C-h, :isearch_delete_char)
|
17
|
+
ISEARCH_MODE_MAP.define_key(?\C-?, :isearch_delete_char)
|
17
18
|
ISEARCH_MODE_MAP.define_key(?\C-s, :isearch_repeat_forward)
|
18
19
|
ISEARCH_MODE_MAP.define_key(?\C-r, :isearch_repeat_backward)
|
19
20
|
ISEARCH_MODE_MAP.define_key(?\C-w, :isearch_yank_word_or_char)
|
20
21
|
ISEARCH_MODE_MAP.define_key(?\C-m, :isearch_exit)
|
21
22
|
ISEARCH_MODE_MAP.define_key(?\C-g, :isearch_abort)
|
23
|
+
ISEARCH_MODE_MAP.define_key(?\C-q, :isearch_quoted_insert)
|
24
|
+
ISEARCH_MODE_MAP.define_key(?\C-\\, :isearch_toggle_input_method)
|
22
25
|
|
23
26
|
ISEARCH_STATUS = {
|
24
27
|
forward: true,
|
@@ -55,6 +58,10 @@ def isearch_mode(forward, recursive_edit: false)
|
|
55
58
|
end
|
56
59
|
end
|
57
60
|
|
61
|
+
def isearch_mode?
|
62
|
+
Controller.current.overriding_map == ISEARCH_MODE_MAP
|
63
|
+
end
|
64
|
+
|
58
65
|
def isearch_prompt
|
59
66
|
if ISEARCH_STATUS[:forward]
|
60
67
|
"I-search: "
|
@@ -114,6 +121,14 @@ def isearch_done
|
|
114
121
|
end
|
115
122
|
end
|
116
123
|
|
124
|
+
define_command(:isearch_quoted_insert, doc: <<~EOD) do
|
125
|
+
Read a character, and add it to the search string and search.
|
126
|
+
EOD
|
127
|
+
c = Controller.current.read_event
|
128
|
+
ISEARCH_STATUS[:string].concat(c)
|
129
|
+
isearch_search
|
130
|
+
end
|
131
|
+
|
117
132
|
def isearch_search
|
118
133
|
forward = ISEARCH_STATUS[:forward]
|
119
134
|
options = if /\A[A-Z]/.match?(ISEARCH_STATUS[:string])
|
@@ -124,7 +139,7 @@ def isearch_search
|
|
124
139
|
re = Regexp.new(Regexp.quote(ISEARCH_STATUS[:string]), options)
|
125
140
|
last_pos = ISEARCH_STATUS[:last_pos]
|
126
141
|
offset = forward ? last_pos : last_pos - ISEARCH_STATUS[:string].bytesize
|
127
|
-
if Buffer.current.byteindex(forward, re, offset)
|
142
|
+
if offset >= 0 && Buffer.current.byteindex(forward, re, offset)
|
128
143
|
if Buffer.current != Buffer.minibuffer
|
129
144
|
message(isearch_prompt + ISEARCH_STATUS[:string], log: false)
|
130
145
|
end
|
@@ -155,5 +170,11 @@ def isearch_repeat(forward)
|
|
155
170
|
end
|
156
171
|
isearch_search
|
157
172
|
end
|
173
|
+
|
174
|
+
define_command(:isearch_toggle_input_method,
|
175
|
+
doc: "Toggle input method.") do
|
176
|
+
toggle_input_method
|
177
|
+
message(isearch_prompt + ISEARCH_STATUS[:string], log: false)
|
178
|
+
end
|
158
179
|
end
|
159
180
|
end
|
@@ -6,7 +6,12 @@ module Commands
|
|
6
6
|
end
|
7
7
|
|
8
8
|
define_command(:exit_textbringer) do |status = 0|
|
9
|
-
|
9
|
+
unsaved_buffers = Buffer.filter { |buffer|
|
10
|
+
/\A\*/ !~ buffer.name && buffer.modified?
|
11
|
+
}
|
12
|
+
if !unsaved_buffers.empty?
|
13
|
+
list_buffers(unsaved_buffers)
|
14
|
+
Window.redisplay
|
10
15
|
return unless yes_or_no?("Unsaved buffers exist; exit anyway?")
|
11
16
|
end
|
12
17
|
exit(status)
|
@@ -139,7 +144,7 @@ def complete_minibuffer_with_string(s)
|
|
139
144
|
end
|
140
145
|
end
|
141
146
|
|
142
|
-
UNIVERSAL_ARGUMENT_MAP
|
147
|
+
define_keymap :UNIVERSAL_ARGUMENT_MAP
|
143
148
|
(?0..?9).each do |c|
|
144
149
|
UNIVERSAL_ARGUMENT_MAP.define_key(c, :digit_argument)
|
145
150
|
GLOBAL_MAP.define_key("\e#{c}", :digit_argument)
|
@@ -276,7 +281,7 @@ def goto_global_mark
|
|
276
281
|
Window.redisplay
|
277
282
|
signals = [:INT, :TERM, :KILL]
|
278
283
|
begin
|
279
|
-
opts = /
|
284
|
+
opts = /mswin|mingw/ =~ RUBY_PLATFORM ? {} : {pgroup: true}
|
280
285
|
if CONFIG[:shell_file_name]
|
281
286
|
cmd = [CONFIG[:shell_file_name], CONFIG[:shell_command_switch], cmd]
|
282
287
|
end
|
@@ -69,9 +69,13 @@ module Commands
|
|
69
69
|
define_command(:switch_to_buffer, doc: <<~EOD) do
|
70
70
|
Display buffer in the current window.
|
71
71
|
EOD
|
72
|
-
|buffer = read_buffer("Switch to buffer: ")|
|
72
|
+
|buffer = read_buffer("Switch to buffer: "), arg = current_prefix_arg|
|
73
73
|
if buffer.is_a?(String)
|
74
|
-
|
74
|
+
if arg
|
75
|
+
buffer = Buffer.find_or_new(buffer)
|
76
|
+
else
|
77
|
+
buffer = Buffer[buffer]
|
78
|
+
end
|
75
79
|
end
|
76
80
|
if buffer
|
77
81
|
Window.current.buffer = Buffer.current = buffer
|
@@ -80,7 +84,7 @@ module Commands
|
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|
83
|
-
define_command(:list_buffers, doc: <<~EOD) do
|
87
|
+
define_command(:list_buffers, doc: <<~EOD) do |buffers = Buffer.list|
|
84
88
|
List the existing buffers.
|
85
89
|
EOD
|
86
90
|
buffer = Buffer.find_or_new("*Buffer List*",
|
@@ -88,7 +92,7 @@ module Commands
|
|
88
92
|
buffer.apply_mode(BufferListMode)
|
89
93
|
buffer.read_only_edit do
|
90
94
|
buffer.clear
|
91
|
-
buffer.insert(
|
95
|
+
buffer.insert(buffers.map(&:name).join("\n"))
|
92
96
|
buffer.beginning_of_buffer
|
93
97
|
end
|
94
98
|
switch_to_buffer(buffer)
|
data/lib/textbringer/config.rb
CHANGED
@@ -264,7 +264,13 @@ def read_event_with_keyboard_macro(read_event_method)
|
|
264
264
|
end
|
265
265
|
|
266
266
|
def call_read_event_method(read_event_method)
|
267
|
-
Window.current.send(read_event_method)
|
267
|
+
Window.current.send(read_event_method)&.then { |event|
|
268
|
+
if @key_sequence.empty?
|
269
|
+
Buffer.current.filter_event(event)
|
270
|
+
else
|
271
|
+
event
|
272
|
+
end
|
273
|
+
}
|
268
274
|
end
|
269
275
|
end
|
270
276
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Textbringer
|
2
|
+
class InputMethod
|
3
|
+
extend Commands
|
4
|
+
include Commands
|
5
|
+
|
6
|
+
@@list = []
|
7
|
+
|
8
|
+
def self.inherited(subclass)
|
9
|
+
name = subclass.name.sub(/Textbringer::/, "").sub(/InputMethod/, "").
|
10
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
11
|
+
downcase
|
12
|
+
@@list.push(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.list
|
16
|
+
@@list
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.find(name)
|
20
|
+
class_name = name.split(/_/).map(&:capitalize).join + "InputMethod"
|
21
|
+
Textbringer.const_get(class_name).new
|
22
|
+
rescue NameError
|
23
|
+
raise EditorError, "No such input method: #{name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@enabled = false
|
28
|
+
@skip_next_event = false
|
29
|
+
end
|
30
|
+
|
31
|
+
def toggle
|
32
|
+
@enabled = !@enabled
|
33
|
+
end
|
34
|
+
|
35
|
+
def disable
|
36
|
+
@enabled = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def enabled?
|
40
|
+
@enabled
|
41
|
+
end
|
42
|
+
|
43
|
+
def filter_event(event)
|
44
|
+
if @enabled
|
45
|
+
if event == "\e"
|
46
|
+
@skip_next_event = true
|
47
|
+
event
|
48
|
+
elsif @skip_next_event
|
49
|
+
@skip_next_event = false
|
50
|
+
event
|
51
|
+
else
|
52
|
+
handle_event(event)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
event
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def handle_event(event)
|
60
|
+
raise EditorError, "subclass must override InputMethod#handle_event"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Textbringer
|
4
|
+
class HiraganaInputMethod < InputMethod
|
5
|
+
HIRAGANA_TABLE = {
|
6
|
+
"a" => "あ", "i" => "い", "u" => "う", "e" => "え", "o" => "お",
|
7
|
+
"ka" => "か", "ki" => "き", "ku" => "く", "ke" => "け", "ko" => "こ",
|
8
|
+
"ga" => "が", "gi" => "ぎ", "gu" => "ぐ", "ge" => "げ", "go" => "ご",
|
9
|
+
"sa" => "さ", "si" => "し", "su" => "す", "se" => "せ", "so" => "そ",
|
10
|
+
"za" => "ざ", "zi" => "じ", "zu" => "ず", "ze" => "ぜ", "zo" => "ぞ",
|
11
|
+
"sha" => "しゃ", "shi" => "し", "shu" => "しゅ", "she" => "しぇ", "sho" => "しょ",
|
12
|
+
"ja" => "じゃ", "ji" => "じ", "ju" => "じゅ", "je" => "じぇ", "jo" => "じょ",
|
13
|
+
"ta" => "た", "ti" => "ち", "tu" => "つ", "te" => "て", "to" => "と",
|
14
|
+
"da" => "だ", "di" => "ぢ", "du" => "づ", "de" => "で", "do" => "ど",
|
15
|
+
"cha" => "ちゃ", "chi" => "ち", "chu" => "ちゅ", "che" => "ちぇ", "cho" => "ちょ",
|
16
|
+
"na" => "な", "ni" => "に", "nu" => "ぬ", "ne" => "ね", "no" => "の",
|
17
|
+
"ha" => "は", "hi" => "ひ", "hu" => "ふ", "he" => "へ", "ho" => "ほ",
|
18
|
+
"ba" => "ば", "bi" => "び", "bu" => "ぶ", "be" => "べ", "bo" => "ぼ",
|
19
|
+
"pa" => "ぱ", "pi" => "ぴ", "pu" => "ぷ", "pe" => "ぺ", "po" => "ぽ",
|
20
|
+
"ma" => "ま", "mi" => "み", "mu" => "む", "me" => "め", "mo" => "も",
|
21
|
+
"ya" => "や", "yi" => "い", "yu" => "ゆ", "ye" => "いぇ", "yo" => "よ",
|
22
|
+
"ra" => "ら", "ri" => "り", "ru" => "る", "re" => "れ", "ro" => "ろ",
|
23
|
+
"wa" => "わ", "wi" => "ゐ", "wu" => "う", "we" => "ゑ", "wo" => "を",
|
24
|
+
"nn" => "ん"
|
25
|
+
}
|
26
|
+
HIRAGANA_PREFIXES = HIRAGANA_TABLE.keys.flat_map { |s|
|
27
|
+
(s.size - 1).times.map { |i| s[0, i + 1] }
|
28
|
+
}.uniq
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
super
|
32
|
+
@input_buffer = +""
|
33
|
+
end
|
34
|
+
|
35
|
+
def status
|
36
|
+
"あ"
|
37
|
+
end
|
38
|
+
|
39
|
+
def handle_event(event)
|
40
|
+
if !event.is_a?(String)
|
41
|
+
if !@input_buffer.empty?
|
42
|
+
@input_buffer = +""
|
43
|
+
end
|
44
|
+
return event
|
45
|
+
end
|
46
|
+
@input_buffer << event
|
47
|
+
s = HIRAGANA_TABLE[@input_buffer]
|
48
|
+
if s
|
49
|
+
return flush(s)
|
50
|
+
end
|
51
|
+
if HIRAGANA_PREFIXES.include?(@input_buffer)
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
flush(@input_buffer)
|
55
|
+
end
|
56
|
+
|
57
|
+
def flush(s)
|
58
|
+
if !@input_buffer.empty?
|
59
|
+
@input_buffer = +""
|
60
|
+
end
|
61
|
+
if s.size == 1
|
62
|
+
s
|
63
|
+
else
|
64
|
+
Buffer.current.insert(s)
|
65
|
+
Window.redisplay
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,458 @@
|
|
1
|
+
module Textbringer
|
2
|
+
class TCodeInputMethod < InputMethod
|
3
|
+
require_relative "t_code_input_method/tables"
|
4
|
+
|
5
|
+
data_dir = File.expand_path("t_code_input_method", __dir__)
|
6
|
+
BUSHU_PATH = File.expand_path("bushu.rev", data_dir)
|
7
|
+
BUSHU_DIC = {} unless defined?(BUSHU_DIC)
|
8
|
+
MAZEGAKI_PATH = File.expand_path("mazegaki.dic", data_dir)
|
9
|
+
MAZEGAKI_DIC = {} unless defined?(MAZEGAKI_DIC)
|
10
|
+
MAZEGAKI_MAX_WORD_LEN = 12 # じょうほうしょりがっかい
|
11
|
+
MAZEGAKI_MAX_SUFFIX_LEN = 4
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super
|
15
|
+
@prev_key_index = nil
|
16
|
+
@mazegaki_start_pos = nil
|
17
|
+
@mazegaki_candidates = nil
|
18
|
+
@delete_help_window = false
|
19
|
+
@help_window = nil
|
20
|
+
@prev_buffer = nil
|
21
|
+
setup_dictionaries
|
22
|
+
end
|
23
|
+
|
24
|
+
def setup_dictionaries
|
25
|
+
data_dir = CONFIG[:t_code_data_dir] ||
|
26
|
+
File.expand_path("~/.textbringer/tcode")
|
27
|
+
bushu_path = File.join(data_dir, "bushu.rev")
|
28
|
+
mazegaki_path = File.join(data_dir, "mazegaki.dic")
|
29
|
+
if BUSHU_DIC.empty?
|
30
|
+
File.open(bushu_path) do |f|
|
31
|
+
f.each_line do |line|
|
32
|
+
x, *xs = line.chomp.chars
|
33
|
+
BUSHU_DIC[xs.sort.join] = x
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
if MAZEGAKI_DIC.empty?
|
38
|
+
File.open(mazegaki_path) do |f|
|
39
|
+
f.each_line do |line|
|
40
|
+
x, y = line.split
|
41
|
+
MAZEGAKI_DIC[x] = y
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def status
|
48
|
+
"漢"
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_event(event)
|
52
|
+
key_index = KEY_TABLE[event]
|
53
|
+
if @mazegaki_start_pos
|
54
|
+
if process_mazegaki_conversion(event, key_index)
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
if key_index.nil?
|
59
|
+
@prev_key_index = nil
|
60
|
+
return event
|
61
|
+
end
|
62
|
+
if @prev_key_index.nil?
|
63
|
+
@prev_key_index = key_index
|
64
|
+
nil
|
65
|
+
else
|
66
|
+
c = KANJI_TABLE[key_index][@prev_key_index]
|
67
|
+
@prev_key_index = nil
|
68
|
+
case c
|
69
|
+
when ?■
|
70
|
+
nil
|
71
|
+
when ?◆
|
72
|
+
bushu_compose
|
73
|
+
when ?◇
|
74
|
+
start_mazegaki_conversion(false)
|
75
|
+
when ?◈
|
76
|
+
start_mazegaki_conversion(true)
|
77
|
+
when ?⑤
|
78
|
+
show_stroke
|
79
|
+
else
|
80
|
+
c
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def with_target_buffer(&block)
|
86
|
+
if isearch_mode?
|
87
|
+
@isearch_buffer ||= Buffer.new
|
88
|
+
if @isearch_buffer.to_s != ISEARCH_STATUS[:string]
|
89
|
+
@isearch_buffer.replace(ISEARCH_STATUS[:string])
|
90
|
+
end
|
91
|
+
block.call(@isearch_buffer)
|
92
|
+
ISEARCH_STATUS[:string] = @isearch_buffer.to_s
|
93
|
+
if Buffer.current != Buffer.minibuffer
|
94
|
+
message(isearch_prompt + ISEARCH_STATUS[:string], log: false)
|
95
|
+
Window.redisplay
|
96
|
+
end
|
97
|
+
else
|
98
|
+
block.call(Buffer.current)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def bushu_compose
|
103
|
+
with_target_buffer do |buffer|
|
104
|
+
pos = buffer.point
|
105
|
+
s = 2.times.map {
|
106
|
+
buffer.backward_char
|
107
|
+
buffer.char_after
|
108
|
+
}.sort.join
|
109
|
+
c = BUSHU_DIC[s]
|
110
|
+
if c
|
111
|
+
buffer.replace(c, start: buffer.point, end: pos)
|
112
|
+
else
|
113
|
+
buffer.goto_char(pos)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
isearch_search if isearch_mode?
|
117
|
+
Window.redisplay
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def start_mazegaki_conversion(with_inflection = false)
|
122
|
+
@mazegaki_convert_with_inflection = with_inflection
|
123
|
+
pos, yomi = find_mazegaki_start_pos(with_inflection)
|
124
|
+
if pos.nil?
|
125
|
+
raise EditorError, "No mazegaki conversion candidate"
|
126
|
+
end
|
127
|
+
mazegaki_convert(pos, yomi)
|
128
|
+
end
|
129
|
+
|
130
|
+
def mazegaki_convert(pos, yomi)
|
131
|
+
with_target_buffer do |buffer|
|
132
|
+
candidates = mazegaki_lookup_candidates(yomi)
|
133
|
+
if candidates
|
134
|
+
@mazegaki_yomi = yomi
|
135
|
+
@mazegaki_suffix = buffer.substring(pos + yomi.bytesize,
|
136
|
+
buffer.point)
|
137
|
+
case candidates.size
|
138
|
+
when 1
|
139
|
+
buffer.composite_edit do
|
140
|
+
buffer.delete_region(pos, buffer.point)
|
141
|
+
buffer.insert("△" + candidates[0] + @mazegaki_suffix)
|
142
|
+
end
|
143
|
+
when 2
|
144
|
+
buffer.composite_edit do
|
145
|
+
buffer.delete_region(pos, buffer.point)
|
146
|
+
buffer.insert("△{" + candidates.join(",") + "}" +
|
147
|
+
@mazegaki_suffix)
|
148
|
+
end
|
149
|
+
else
|
150
|
+
buffer.save_excursion do
|
151
|
+
buffer.goto_char(pos)
|
152
|
+
buffer.insert("△")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
@mazegaki_start_pos = pos
|
156
|
+
@mazegaki_candidates = candidates
|
157
|
+
@mazegaki_candidates_page = 0
|
158
|
+
if candidates.size > 2
|
159
|
+
show_mazegaki_candidates
|
160
|
+
end
|
161
|
+
end
|
162
|
+
Window.redisplay
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def mazegaki_lookup_yomi(s, with_inflectin)
|
168
|
+
if !with_inflectin
|
169
|
+
return MAZEGAKI_DIC.key?(s) ? s : nil
|
170
|
+
end
|
171
|
+
yomi = s.sub(/\p{hiragana}\z/, "")
|
172
|
+
(MAZEGAKI_MAX_SUFFIX_LEN + 1).times do
|
173
|
+
return yomi if MAZEGAKI_DIC.key?(yomi + "—")
|
174
|
+
break if !yomi.sub!(/\p{hiragana}\z/, "")
|
175
|
+
end
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
|
179
|
+
def mazegaki_lookup_candidates(yomi)
|
180
|
+
if @mazegaki_convert_with_inflection
|
181
|
+
s = yomi + "—"
|
182
|
+
else
|
183
|
+
s = yomi
|
184
|
+
end
|
185
|
+
c = MAZEGAKI_DIC[s]
|
186
|
+
return nil if c.nil?
|
187
|
+
candidates = c.split("/").map { |i|
|
188
|
+
i.sub(/;.*/, "")
|
189
|
+
}.reject(&:empty?)
|
190
|
+
return nil if candidates.empty?
|
191
|
+
candidates
|
192
|
+
end
|
193
|
+
|
194
|
+
def find_mazegaki_start_pos(with_inflection)
|
195
|
+
with_target_buffer do |buffer|
|
196
|
+
buffer.save_excursion do
|
197
|
+
pos = buffer.point
|
198
|
+
start_pos = nil
|
199
|
+
yomi = nil
|
200
|
+
MAZEGAKI_MAX_WORD_LEN.times do
|
201
|
+
break if buffer.beginning_of_buffer?
|
202
|
+
buffer.backward_char
|
203
|
+
s = buffer.substring(buffer.point, pos)
|
204
|
+
y = mazegaki_lookup_yomi(s, with_inflection)
|
205
|
+
if y
|
206
|
+
start_pos = buffer.point
|
207
|
+
yomi = y
|
208
|
+
end
|
209
|
+
end
|
210
|
+
return start_pos, yomi
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def process_mazegaki_conversion(event, key_index)
|
216
|
+
case event
|
217
|
+
when " "
|
218
|
+
mazegaki_next_page
|
219
|
+
return true
|
220
|
+
when "<"
|
221
|
+
mazegaki_relimit_left
|
222
|
+
return true
|
223
|
+
when ">"
|
224
|
+
mazegaki_relimit_right
|
225
|
+
return true
|
226
|
+
end
|
227
|
+
begin
|
228
|
+
if @mazegaki_candidates.size == 1
|
229
|
+
if event == "\C-m"
|
230
|
+
mazegaki_finish(@mazegaki_candidates[0])
|
231
|
+
return true
|
232
|
+
elsif key_index
|
233
|
+
mazegaki_finish(@mazegaki_candidates[0])
|
234
|
+
return false
|
235
|
+
end
|
236
|
+
elsif key_index
|
237
|
+
mazegaki_limit = MAZEGAKI_STROKE_PRIORITY_LIST.size
|
238
|
+
i = MAZEGAKI_STROKE_PRIORITY_LIST.index(key_index)
|
239
|
+
if i
|
240
|
+
offset = @mazegaki_candidates_page * mazegaki_limit + i
|
241
|
+
c = @mazegaki_candidates[offset]
|
242
|
+
if c
|
243
|
+
mazegaki_finish(c)
|
244
|
+
return true
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
mazegaki_reset
|
249
|
+
true
|
250
|
+
ensure
|
251
|
+
@mazegaki_start_pos = nil
|
252
|
+
@mazegaki_candidates = nil
|
253
|
+
Window.redisplay
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def mazegaki_reset
|
258
|
+
with_target_buffer do |buffer|
|
259
|
+
buffer.undo
|
260
|
+
pos = @mazegaki_start_pos +
|
261
|
+
@mazegaki_yomi.bytesize + @mazegaki_suffix.bytesize
|
262
|
+
buffer.goto_char(pos)
|
263
|
+
hide_help_window
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def mazegaki_finish(s)
|
268
|
+
mazegaki_reset
|
269
|
+
with_target_buffer do |buffer|
|
270
|
+
buffer.composite_edit do
|
271
|
+
buffer.delete_region(@mazegaki_start_pos, buffer.point)
|
272
|
+
buffer.insert(s + @mazegaki_suffix)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
isearch_search if isearch_mode?
|
276
|
+
end
|
277
|
+
|
278
|
+
def hide_help_window
|
279
|
+
if @delete_help_window
|
280
|
+
Window.delete_window(@help_window)
|
281
|
+
elsif @prev_buffer
|
282
|
+
@help_window.buffer = @prev_buffer
|
283
|
+
end
|
284
|
+
@delete_help_window = false
|
285
|
+
@help_window = nil
|
286
|
+
@prev_buffer = nil
|
287
|
+
end
|
288
|
+
|
289
|
+
def mazegaki_next_page
|
290
|
+
if @mazegaki_candidates.size <= mazegaki_limit
|
291
|
+
return
|
292
|
+
end
|
293
|
+
@mazegaki_candidates_page += 1
|
294
|
+
if @mazegaki_candidates_page * mazegaki_limit >
|
295
|
+
@mazegaki_candidates.size
|
296
|
+
@mazegaki_candidates_page = 0
|
297
|
+
end
|
298
|
+
show_mazegaki_candidates
|
299
|
+
Window.redisplay
|
300
|
+
end
|
301
|
+
|
302
|
+
def mazegaki_relimit_left
|
303
|
+
with_target_buffer do |buffer|
|
304
|
+
yomi = nil
|
305
|
+
start_pos = nil
|
306
|
+
mazegaki_reset
|
307
|
+
buffer.save_excursion do
|
308
|
+
pos = buffer.point
|
309
|
+
buffer.goto_char(@mazegaki_start_pos)
|
310
|
+
s = buffer.substring(buffer.point, pos)
|
311
|
+
(MAZEGAKI_MAX_WORD_LEN - s.size).times do
|
312
|
+
break if buffer.beginning_of_buffer?
|
313
|
+
buffer.backward_char
|
314
|
+
s = buffer.substring(buffer.point, pos)
|
315
|
+
yomi = mazegaki_lookup_yomi(s, @mazegaki_convert_with_inflection)
|
316
|
+
if yomi
|
317
|
+
start_pos = buffer.point
|
318
|
+
break
|
319
|
+
end
|
320
|
+
end
|
321
|
+
if start_pos.nil?
|
322
|
+
message("Can't relimit left")
|
323
|
+
start_pos = @mazegaki_start_pos
|
324
|
+
yomi = @mazegaki_yomi
|
325
|
+
end
|
326
|
+
end
|
327
|
+
mazegaki_convert(start_pos, yomi)
|
328
|
+
Window.redisplay
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def mazegaki_relimit_right
|
333
|
+
with_target_buffer do |buffer|
|
334
|
+
start_pos = nil
|
335
|
+
yomi = nil
|
336
|
+
mazegaki_reset
|
337
|
+
buffer.save_excursion do
|
338
|
+
pos = buffer.point
|
339
|
+
buffer.goto_char(@mazegaki_start_pos)
|
340
|
+
if @mazegaki_convert_with_inflection && @mazegaki_yomi &&
|
341
|
+
(yomi = mazegaki_lookup_yomi(@mazegaki_yomi, true))
|
342
|
+
start_pos = @mazegaki_start_pos
|
343
|
+
else
|
344
|
+
loop do
|
345
|
+
break if buffer.point >= pos
|
346
|
+
buffer.forward_char
|
347
|
+
s = buffer.substring(buffer.point, pos)
|
348
|
+
yomi = mazegaki_lookup_yomi(s, @mazegaki_convert_with_inflection)
|
349
|
+
if yomi
|
350
|
+
start_pos = buffer.point
|
351
|
+
break
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
if start_pos.nil?
|
357
|
+
if !@mazegaki_convert_with_inflection
|
358
|
+
start_pos, yomi = find_mazegaki_start_pos(true)
|
359
|
+
if start_pos
|
360
|
+
@mazegaki_convert_with_inflection = true
|
361
|
+
end
|
362
|
+
end
|
363
|
+
if start_pos.nil?
|
364
|
+
message("Can't relimit right")
|
365
|
+
start_pos = @mazegaki_start_pos
|
366
|
+
yomi = @mazegaki_yomi
|
367
|
+
end
|
368
|
+
end
|
369
|
+
mazegaki_convert(start_pos, yomi)
|
370
|
+
Window.redisplay
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def show_mazegaki_candidates
|
375
|
+
offset = @mazegaki_candidates_page * mazegaki_limit
|
376
|
+
candidates = @mazegaki_candidates[offset, mazegaki_limit]
|
377
|
+
xs = Array.new(40, "-")
|
378
|
+
candidates.each_with_index do |s, i|
|
379
|
+
xs[MAZEGAKI_STROKE_PRIORITY_LIST[i]] = s
|
380
|
+
end
|
381
|
+
max_width = candidates.map { |s|
|
382
|
+
Buffer.display_width(s)
|
383
|
+
}.max
|
384
|
+
page = @mazegaki_candidates_page + 1
|
385
|
+
page_count =
|
386
|
+
(@mazegaki_candidates.size.to_f / mazegaki_limit).ceil
|
387
|
+
message = xs.map.with_index { |s, i|
|
388
|
+
space = " " * (max_width - Buffer.display_width(s))
|
389
|
+
if i % 10 < 5
|
390
|
+
s + space
|
391
|
+
else
|
392
|
+
space + s
|
393
|
+
end
|
394
|
+
}.each_slice(10).map.with_index { |ys, i|
|
395
|
+
if i == 0
|
396
|
+
" " + ys[0, 4].join(" ") + " " + ys[4, 2].join(" ") + " " +
|
397
|
+
ys[6, 4].join(" ")
|
398
|
+
else
|
399
|
+
"[" + ys[0, 4].join(" ") + "] " + ys[4, 2].join(" ") + " [" +
|
400
|
+
ys[6, 4].join(" ") + "]"
|
401
|
+
end
|
402
|
+
}.join("\n") + " (#{page}/#{page_count})"
|
403
|
+
show_help(message)
|
404
|
+
end
|
405
|
+
|
406
|
+
def mazegaki_limit
|
407
|
+
MAZEGAKI_STROKE_PRIORITY_LIST.size
|
408
|
+
end
|
409
|
+
|
410
|
+
def show_stroke
|
411
|
+
c = Buffer.current.char_after
|
412
|
+
x, y = KANJI_TABLE.find.with_index { |row, i|
|
413
|
+
j = row.index(c)
|
414
|
+
if j
|
415
|
+
break [j, i]
|
416
|
+
else
|
417
|
+
false
|
418
|
+
end
|
419
|
+
}
|
420
|
+
if x.nil?
|
421
|
+
raise EditorError, "Stroke not found"
|
422
|
+
end
|
423
|
+
s = " " * 10 + "・・・・ ・・・・" * 3
|
424
|
+
s[x] = "1"
|
425
|
+
s[y] = "2"
|
426
|
+
message = s.gsub(/.{10}/, "\\&\n").gsub(/ /, " ")
|
427
|
+
show_help(message)
|
428
|
+
Window.redisplay
|
429
|
+
nil
|
430
|
+
end
|
431
|
+
|
432
|
+
def show_help(message)
|
433
|
+
buffer = Buffer.find_or_new("*T-Code Help*",
|
434
|
+
undo_limit: 0, read_only: true)
|
435
|
+
buffer.read_only_edit do
|
436
|
+
buffer.clear
|
437
|
+
buffer.insert(message)
|
438
|
+
buffer.beginning_of_buffer
|
439
|
+
end
|
440
|
+
if Window.list.size == 1
|
441
|
+
Window.list.first.split(message.lines.size + 1)
|
442
|
+
@delete_help_window = true
|
443
|
+
end
|
444
|
+
if Window.current.echo_area?
|
445
|
+
window = Window.list.last
|
446
|
+
else
|
447
|
+
windows = Window.list
|
448
|
+
i = (windows.index(Window.current) + 1) % windows.size
|
449
|
+
window = windows[i]
|
450
|
+
end
|
451
|
+
@help_window = window
|
452
|
+
if window.buffer != buffer
|
453
|
+
@prev_buffer = window.buffer
|
454
|
+
window.buffer = buffer
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|