textbringer 1.0.4 → 1.0.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.
- checksums.yaml +4 -4
- data/.github/workflows/ubuntu.yml +1 -1
- data/.github/workflows/windows.yml +1 -1
- data/CHANGES.md +24 -0
- data/bin/merge_mazegaki_dic +23 -0
- data/exe/textbringer +4 -3
- data/lib/textbringer.rb +4 -0
- data/lib/textbringer/buffer.rb +37 -0
- data/lib/textbringer/commands/files.rb +8 -0
- data/lib/textbringer/commands/input_method.rb +18 -0
- data/lib/textbringer/commands/isearch.rb +13 -1
- data/lib/textbringer/commands/windows.rb +6 -2
- 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 +1 -0
- data/lib/textbringer/modes/ruby_mode.rb +41 -5
- data/lib/textbringer/utils.rb +3 -1
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +22 -11
- data/textbringer.gemspec +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42dc4dbbf6e190e38d32657942af39c2bf5afec5035a6674d54a7d621c755086
|
4
|
+
data.tar.gz: dbaa67ad2fca2b24d661610e7467b94022da87928473a2a37a7169deda7a27ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c765b54dc47b490c8fe9d53b84b00734281a1d5e5e22714eaba7c9a69c71480271fb0b34a2883fed4f7ad4be1fc2933871e1c0fe5585e16e12b87f569a8ee662
|
7
|
+
data.tar.gz: '0684345317fcf495d5cf0f7951e120f88f24f025f8c14960dac33874c131f70f2c6a24e1ee6b8b8774783deea8b80c9047a063a8f0fe79666824578e5e54f5dc'
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
## 1.0.9
|
2
|
+
|
3
|
+
* Remove mazegaki.dic and bushu.rev
|
4
|
+
|
5
|
+
## 1.0.8
|
6
|
+
|
7
|
+
* Updated mazegaki.dic
|
8
|
+
* Add licenses of dictionary data to LEGAL.txt
|
9
|
+
|
10
|
+
## 1.0.7
|
11
|
+
|
12
|
+
* Support endless method definitions in ruby-mode.
|
13
|
+
* Updated mazegaki.dic.
|
14
|
+
|
15
|
+
## 1.0.6
|
16
|
+
|
17
|
+
* Add the Hiragana input method.
|
18
|
+
* Add make_directory.
|
19
|
+
* Bug fixes.
|
20
|
+
|
21
|
+
## 1.0.5
|
22
|
+
|
23
|
+
* Support the Japanese input method T-Code.
|
24
|
+
|
1
25
|
## 1.0.4
|
2
26
|
|
3
27
|
* Support Ruby 3.0.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
MAZEGAKI_DIC = Hash.new([])
|
4
|
+
|
5
|
+
ARGF.each_line do |line|
|
6
|
+
next if /^\p{ascii}/.match?(line)
|
7
|
+
x, y = line.split
|
8
|
+
key = x.sub(/\A(\p{hiragana}+)[a-z>]\z/, "\\1—")
|
9
|
+
values = y.split("/").map { |i|
|
10
|
+
i.sub(/;.*/, "")
|
11
|
+
}.reject { |i|
|
12
|
+
i.empty? || i == key
|
13
|
+
}
|
14
|
+
MAZEGAKI_DIC[key] |= values
|
15
|
+
end
|
16
|
+
|
17
|
+
MAZEGAKI_DIC.sort_by { |key,|
|
18
|
+
key
|
19
|
+
}.each do |key, values|
|
20
|
+
puts "#{key} /#{values.join('/')}/"
|
21
|
+
end
|
22
|
+
|
23
|
+
|
data/exe/textbringer
CHANGED
@@ -5,8 +5,8 @@ require "textbringer"
|
|
5
5
|
include Textbringer
|
6
6
|
include Commands
|
7
7
|
|
8
|
-
def load_user_config
|
9
|
-
config_file = File.expand_path(
|
8
|
+
def load_user_config(path)
|
9
|
+
config_file = File.expand_path(path)
|
10
10
|
begin
|
11
11
|
load(config_file)
|
12
12
|
rescue LoadError
|
@@ -24,8 +24,9 @@ Controller.current = Controller.new
|
|
24
24
|
begin
|
25
25
|
Window.start do
|
26
26
|
begin
|
27
|
+
load_user_config("~/.textbringer/init.rb")
|
27
28
|
Plugin.load_plugins
|
28
|
-
load_user_config
|
29
|
+
load_user_config("~/.textbringer.rb")
|
29
30
|
ruby_mode
|
30
31
|
if ARGV.size > 0
|
31
32
|
ARGV.each do |arg|
|
data/lib/textbringer.rb
CHANGED
@@ -22,6 +22,7 @@
|
|
22
22
|
require_relative "textbringer/commands/keyboard_macro"
|
23
23
|
require_relative "textbringer/commands/fill"
|
24
24
|
require_relative "textbringer/commands/server"
|
25
|
+
require_relative "textbringer/commands/input_method"
|
25
26
|
require_relative "textbringer/commands/help"
|
26
27
|
require_relative "textbringer/mode"
|
27
28
|
require_relative "textbringer/modes/fundamental_mode"
|
@@ -32,5 +33,8 @@
|
|
32
33
|
require_relative "textbringer/modes/completion_list_mode"
|
33
34
|
require_relative "textbringer/modes/buffer_list_mode"
|
34
35
|
require_relative "textbringer/modes/help_mode"
|
36
|
+
require_relative "textbringer/input_method"
|
37
|
+
require_relative "textbringer/input_methods/t_code_input_method"
|
38
|
+
require_relative "textbringer/input_methods/hiragana_input_method"
|
35
39
|
require_relative "textbringer/plugin"
|
36
40
|
require_relative "textbringer/controller"
|
data/lib/textbringer/buffer.rb
CHANGED
@@ -11,6 +11,7 @@ class Buffer
|
|
11
11
|
attr_accessor :mode, :keymap
|
12
12
|
attr_reader :name, :file_name, :file_encoding, :file_format, :point, :marks
|
13
13
|
attr_reader :current_line, :current_column, :visible_mark
|
14
|
+
attr_reader :input_method
|
14
15
|
|
15
16
|
GAP_SIZE = 256
|
16
17
|
UNDO_LIMIT = 1000
|
@@ -240,6 +241,7 @@ def initialize(s = +"", name: nil,
|
|
240
241
|
@visible_mark = nil
|
241
242
|
@read_only = read_only
|
242
243
|
@callbacks = {}
|
244
|
+
@input_method = nil
|
243
245
|
end
|
244
246
|
|
245
247
|
def inspect
|
@@ -410,6 +412,7 @@ def save(file_name = @file_name)
|
|
410
412
|
f.flock(File::LOCK_EX)
|
411
413
|
write_to_file(f)
|
412
414
|
f.flush
|
415
|
+
f.fsync
|
413
416
|
end
|
414
417
|
@file_mtime = File.mtime(file_name)
|
415
418
|
rescue Errno::EISDIR
|
@@ -1384,6 +1387,35 @@ def insert_final_newline
|
|
1384
1387
|
end
|
1385
1388
|
end
|
1386
1389
|
|
1390
|
+
def toggle_input_method(name)
|
1391
|
+
if name.nil?
|
1392
|
+
@input_method ||= InputMethod.find(CONFIG[:default_input_method])
|
1393
|
+
else
|
1394
|
+
@input_method = InputMethod.find(name)
|
1395
|
+
end
|
1396
|
+
@input_method.toggle
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
def disable_input_method
|
1400
|
+
@input_method&.disable
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
def filter_event(event)
|
1404
|
+
if @input_method
|
1405
|
+
@input_method.filter_event(event)
|
1406
|
+
else
|
1407
|
+
event
|
1408
|
+
end
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
def input_method_status
|
1412
|
+
if @input_method&.enabled?
|
1413
|
+
@input_method.status
|
1414
|
+
else
|
1415
|
+
"--"
|
1416
|
+
end
|
1417
|
+
end
|
1418
|
+
|
1387
1419
|
private
|
1388
1420
|
|
1389
1421
|
def set_contents(s, enc)
|
@@ -1649,6 +1681,7 @@ class InsertAction < UndoableAction
|
|
1649
1681
|
def initialize(buffer, location, string)
|
1650
1682
|
super(buffer, location)
|
1651
1683
|
@string = string
|
1684
|
+
@copied = false
|
1652
1685
|
end
|
1653
1686
|
|
1654
1687
|
def undo
|
@@ -1662,6 +1695,10 @@ def redo
|
|
1662
1695
|
end
|
1663
1696
|
|
1664
1697
|
def merge(s)
|
1698
|
+
unless @copied
|
1699
|
+
@string = @string.dup
|
1700
|
+
@copied = true
|
1701
|
+
end
|
1665
1702
|
@string.concat(s)
|
1666
1703
|
end
|
1667
1704
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "editorconfig"
|
2
|
+
require "fileutils"
|
2
3
|
|
3
4
|
module Textbringer
|
4
5
|
module Commands
|
@@ -131,5 +132,12 @@ module Commands
|
|
131
132
|
default: Buffer.current.file_name)|
|
132
133
|
find_file(file_name)
|
133
134
|
end
|
135
|
+
|
136
|
+
define_command(:make_directory, doc: "Create a new directory.") do
|
137
|
+
|dir_name = read_file_name("Make directory: ",
|
138
|
+
default: Buffer.current.file_name &&
|
139
|
+
File.dirname(Buffer.current.file_name))|
|
140
|
+
FileUtils.mkdir_p(dir_name)
|
141
|
+
end
|
134
142
|
end
|
135
143
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Textbringer
|
2
|
+
module Commands
|
3
|
+
define_command(:toggle_input_method,
|
4
|
+
doc: "Toggel input method.") do |name = nil|
|
5
|
+
if name.nil? && current_prefix_arg
|
6
|
+
name = read_input_method_name("Input method: ")
|
7
|
+
end
|
8
|
+
Buffer.current.toggle_input_method(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def read_input_method_name(prompt, default: CONFIG[:default_input_method])
|
12
|
+
f = ->(s) {
|
13
|
+
complete_for_minibuffer(s.tr("-", "_"), InputMethod.list)
|
14
|
+
}
|
15
|
+
read_from_minibuffer(prompt, completion_proc: f, default: default)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -14,12 +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)
|
22
23
|
ISEARCH_MODE_MAP.define_key(?\C-q, :isearch_quoted_insert)
|
24
|
+
ISEARCH_MODE_MAP.define_key(?\C-\\, :isearch_toggle_input_method)
|
23
25
|
|
24
26
|
ISEARCH_STATUS = {
|
25
27
|
forward: true,
|
@@ -56,6 +58,10 @@ def isearch_mode(forward, recursive_edit: false)
|
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
61
|
+
def isearch_mode?
|
62
|
+
Controller.current.overriding_map == ISEARCH_MODE_MAP
|
63
|
+
end
|
64
|
+
|
59
65
|
def isearch_prompt
|
60
66
|
if ISEARCH_STATUS[:forward]
|
61
67
|
"I-search: "
|
@@ -133,7 +139,7 @@ def isearch_search
|
|
133
139
|
re = Regexp.new(Regexp.quote(ISEARCH_STATUS[:string]), options)
|
134
140
|
last_pos = ISEARCH_STATUS[:last_pos]
|
135
141
|
offset = forward ? last_pos : last_pos - ISEARCH_STATUS[:string].bytesize
|
136
|
-
if Buffer.current.byteindex(forward, re, offset)
|
142
|
+
if offset >= 0 && Buffer.current.byteindex(forward, re, offset)
|
137
143
|
if Buffer.current != Buffer.minibuffer
|
138
144
|
message(isearch_prompt + ISEARCH_STATUS[:string], log: false)
|
139
145
|
end
|
@@ -164,5 +170,11 @@ def isearch_repeat(forward)
|
|
164
170
|
end
|
165
171
|
isearch_search
|
166
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
|
167
179
|
end
|
168
180
|
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
|
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
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Textbringer
|
2
|
+
class TCodeInputMethod
|
3
|
+
KEYBOARD = "1234567890" "qwertyuiop" "asdfghjkl;" "zxcvbnm,./"
|
4
|
+
|
5
|
+
KEY_TABLE = KEYBOARD.each_char.with_index.map { |c, i|
|
6
|
+
[c, i]
|
7
|
+
}.to_h
|
8
|
+
|
9
|
+
KANJI_TABLE = [
|
10
|
+
"■■■■■■■■■■ヮヰヱヵヶ請境系探象ゎゐゑ■■盛革突温捕■■■■■依繊借須訳",
|
11
|
+
"■■■■■■■■■■丑臼宴縁曳尚賀岸責漁於汚乙穏■益援周域荒■■■■■織父枚乱香",
|
12
|
+
"■■■■■■■■■■鬼虚狭脅驚舎喜幹丘糖奇既菊却享康徒景処ぜ■■■■■譲ヘ模降走",
|
13
|
+
"■■■■■■■■■■孤誇黄后耕布苦圧恵固巧克懇困昏邦舞雑漢緊■■■■■激干彦均又",
|
14
|
+
"■■■■⑤■■■■■奉某貌卜■姿絶密秘押■■■■■衆節杉肉除■■■■■測血散笑弁",
|
15
|
+
"■■■■■■■■■■湖礼著移郷■■■■■償欧努底亜■■■■■禁硝樹句礎■■■■■",
|
16
|
+
"■■■■■■■■■■端飾郵塩群■星析遷宣紅傷豪維脱鼠曹奏尊■絹被源願臨■■■■■",
|
17
|
+
"■■■■◈■■■■■刷寿順危砂庶粧丈称蒸舗充喫腕暴■■■■■批慶渉竜併■■■■■",
|
18
|
+
"■■■■■■■■■■震扱片札乞■乃如尼帳輪倒操柄魚■■■■■就駐揮丹鮮■■■■■",
|
19
|
+
"■■■■■■■■■■弘痛票訴遺欄龍略慮累則存倍牛釈■■■■■綱潟創背皮■■■■■",
|
20
|
+
"ヲ哀暇啓把酸昼炭稲湯果告策首農歩回務島開報紙館夜位給員ど代レ欠夏彼妻善相家的対歴",
|
21
|
+
"ゥ逢牙掲伐貿捜異隣旧概買詳由死キせ区百木音王放々応分よル千ア財針裏居差付プばュ作",
|
22
|
+
"ヴ宛壊携避攻焼闘奈夕武残両在!や出タ手保案曲情引職7か(トれ従骨厚顔量内工八テ見",
|
23
|
+
"ヂ囲較劇卑盤帯易速拡風階能論増コ山者発立横興刺側覚きっ日国二適類御宇推九名川機チ",
|
24
|
+
"ヅ庵寒賢藩汽換延雪互細古利ペゃナ金マ和女崎白ぐ官球上く8え年母奥因酒伸サ建パ第入",
|
25
|
+
"簡徴触宗植■索射濁慢害賃整軽評佐法数郎談服声任検豊美題井洋実爆仲茶率比昔短岩巨敗",
|
26
|
+
"承章候途複■冊需詑迷撃折追隊角接備最急験変審改昇芸宿制集安画陽構旅施曜遠ォ将ぞ塚",
|
27
|
+
"快否歯筆里■皿輯蓄戻浴秀糸春幸記朝知ワ送限研労統役セ運ツ特谷ァ導認健尾序振練念働",
|
28
|
+
"包納頼逃寝■賛瞬貯羊積程断低減モ資士費ィ逆企精ざ印神び打勤ャ殺負何履般耳授版効視",
|
29
|
+
"唱暮憲勉罪■■盾虫■故鉱提児敷無石屋解募令違装然確優公品語演券悪秋非便示即難普辺",
|
30
|
+
"ぱ慰我兼菱桜瀬鳥催障収際太園船中スもお定種岡結進真3と〇てるヒ江別考権ッ人三京ち",
|
31
|
+
"ぴ為掛嫌紐典博筋忠乳若雄査ふ賞わラ東生ろ宅熟待取科ーした一が及久蔵早造ロク万方フ",
|
32
|
+
"ぷ陰敢顕描採謡希仏察指氏丸続ェう4)十リ料土活ね参い、の51投義算半県んまンつ四",
|
33
|
+
"ぺ隠甘牽憤君純副盟標ぎ格次習火あこ6学月受予切育池。◆0・2込沢軍青清けイす電地",
|
34
|
+
"ぽ胃患厳弊犯余堀肩療思術広門聞本さら高シ英ボ加室少ではになを転空性使級業時「長み",
|
35
|
+
"朱遅甲致汎■衰滋沈己病終起路越む南原駅物勢必講愛管要設水藤有素兵専親寮ホ共ブ平楽",
|
36
|
+
"陣鶴鹿貨絡■趨湿添已常張薬防得ケ式戦関男輸形助◇流連鉄教力ベ毛永申袋良私ゴ来信午",
|
37
|
+
"眼繁誌招季■垂甚徹巳寺質づ港条話座線ダ橋基好味宝争デ現エ他度等浅頃落命村ガ製校ご",
|
38
|
+
"執紹夢卸阿■粋■爪巴停領容玉右べ民ソ点遇足草築観言車成天世文板客師税飛ノ完重約各",
|
39
|
+
"岳刑弱雲窓■寸瞳陶■河置供試席期ゾ歳強係婦段衛額渋主映書可へ伝庭課着坂近外米ョ光",
|
40
|
+
"ぁ■瓦■■呼幅歓功盗徳渡守登退店持町所ほ件友卒初慣行ド円小ジヨ誤証含%海道ず西げ",
|
41
|
+
"ぃ■■■■紀破郡抗幡械刊訪融雨全じ自議明宮伊求技写通カ社野同判規感値ギ当理メウグ",
|
42
|
+
"ぅ■■■■房績識属衣帝始了極熱バ部六経動局頭配黒院だり—め大済吉ゆ器照不合面政オ",
|
43
|
+
"ぇ■■■■去疑ぢ綿離読鈴恐督況後間場ニ産向府富直倉新」9子五説週号葉派委化ビ目市",
|
44
|
+
"ぉ■■■■秒範核影麻族丁未才返問ム七住北割ぶ番望元事田会前そ休省央福毎気売下都株",
|
45
|
+
"欲巣茂述朗■■■■■帰庁昨跡ゲ洗羽個医静億録赤想消支協用表正図挙険ゼ波ヤ心界意今",
|
46
|
+
"迫災恋脳老■■■■■監寄裁達芝響忘討史環色貸販編仕先多商ハ交之末ぼ街免再ネ〜口台",
|
47
|
+
"留列刻豆看■■■■■竹注介具失司迎華許補左態花栄ザ調混ポ決ミ州払乗庫状団計夫食総",
|
48
|
+
"替沼?辞献■■■■■ゅ修究答養復並浦ユ冷ぬ展警型誰組選党択体例満津準遊戸ひょ価与",
|
49
|
+
"還更占箱矢■■■■■志抜航層深担陸巻競護根様独止堂銀以ヌ営治字材過諸単身ピ勝反ズ"
|
50
|
+
]
|
51
|
+
|
52
|
+
# Key positions
|
53
|
+
# 0 1 2 3 4 5 6 7 8 9
|
54
|
+
# 10 11 12 13 14 15 16 17 18 19
|
55
|
+
# 20 21 22 23 24 25 26 27 28 29
|
56
|
+
# 30 31 32 33 34 35 36 37 38 39
|
57
|
+
MAZEGAKI_STROKE_PRIORITY_LIST = [
|
58
|
+
22, 23, 21, 24, 20,
|
59
|
+
12, 13, 11, 14, 10,
|
60
|
+
27, 26, 28, 25, 29,
|
61
|
+
17, 16, 18, 15, 19
|
62
|
+
]
|
63
|
+
end
|
64
|
+
end
|
data/lib/textbringer/keymap.rb
CHANGED
@@ -211,6 +211,7 @@ def kbd(key)
|
|
211
211
|
GLOBAL_MAP.define_key([:f1, "f"], :describe_command)
|
212
212
|
GLOBAL_MAP.define_key([:f1, "k"], :describe_key)
|
213
213
|
GLOBAL_MAP.define_key("\C-x#", :server_edit_done)
|
214
|
+
GLOBAL_MAP.define_key("\C-\\", :toggle_input_method)
|
214
215
|
GLOBAL_MAP.handle_undefined_key do |key|
|
215
216
|
if key.is_a?(String) && /[\0-\x7f]/ !~ key
|
216
217
|
:self_insert
|
@@ -21,7 +21,7 @@ class | module | def | undef | begin | rescue | ensure | end |
|
|
21
21
|
if | unless | then | elsif | else | case | when | while | until |
|
22
22
|
for | break | next | redo | retry | in | do | return | yield |
|
23
23
|
super | self | nil | true | false | and | or | not | alias
|
24
|
-
) \b | defined\? )
|
24
|
+
) \b (?![!?]) | defined\? )
|
25
25
|
/x
|
26
26
|
|
27
27
|
define_syntax :string, /
|
@@ -273,6 +273,7 @@ def calculate_indentation
|
|
273
273
|
}
|
274
274
|
if start_with_period ||
|
275
275
|
(last_event == :on_op && last_text != "|") ||
|
276
|
+
(last_event == :on_kw && /\A(and|or)\z/.match?(last_text)) ||
|
276
277
|
last_event == :on_period ||
|
277
278
|
(last_event == :on_comma && event != :on_lbrace &&
|
278
279
|
event != :on_lparen && event != :on_lbracket)
|
@@ -300,10 +301,12 @@ def find_nearest_beginning_token(tokens)
|
|
300
301
|
case text
|
301
302
|
when "class", "module", "def", "if", "unless", "case",
|
302
303
|
"do", "for", "while", "until", "begin"
|
303
|
-
if /\A(if|unless|while|until)\z/.match?(text)
|
304
|
-
|
305
|
-
|
306
|
-
|
304
|
+
if /\A(if|unless|while|until)\z/.match?(text) &&
|
305
|
+
modifier?(tokens, i)
|
306
|
+
next
|
307
|
+
end
|
308
|
+
if text == "def" && endless_method_def?(tokens, i)
|
309
|
+
next
|
307
310
|
end
|
308
311
|
if stack.empty?
|
309
312
|
return i
|
@@ -330,6 +333,39 @@ def find_nearest_beginning_token(tokens)
|
|
330
333
|
return nil, stack.grep_v(/[)\]]/).size
|
331
334
|
end
|
332
335
|
|
336
|
+
def modifier?(tokens, i)
|
337
|
+
(line,), = tokens[i]
|
338
|
+
ts = tokens[0...i].reverse_each.take_while { |(l,_),| l == line }
|
339
|
+
t = ts.find { |_, e| e != :on_sp }
|
340
|
+
t && !(t[1] == :on_op && t[2] == "=")
|
341
|
+
end
|
342
|
+
|
343
|
+
def endless_method_def?(tokens, i)
|
344
|
+
ts = tokens.drop(i + 1)
|
345
|
+
ts.shift while ts[0][1] == :on_sp
|
346
|
+
_, event = ts.shift
|
347
|
+
return false if event != :on_ident
|
348
|
+
ts.shift while ts[0][1] == :on_sp
|
349
|
+
if ts[0][1] == :on_lparen
|
350
|
+
ts.shift
|
351
|
+
count = 1
|
352
|
+
while count > 0
|
353
|
+
_, event = ts.shift
|
354
|
+
return false if event.nil?
|
355
|
+
case event
|
356
|
+
when :on_lparen
|
357
|
+
count +=1
|
358
|
+
when :on_rparen
|
359
|
+
count -=1
|
360
|
+
end
|
361
|
+
end
|
362
|
+
ts.shift while ts[0][1] == :on_sp
|
363
|
+
end
|
364
|
+
ts[0][1] == :on_op && ts[0][2] == "="
|
365
|
+
rescue NoMethodError # no token
|
366
|
+
return false
|
367
|
+
end
|
368
|
+
|
333
369
|
def find_test_target_path(base, namespace, name)
|
334
370
|
patterns = []
|
335
371
|
if namespace
|
data/lib/textbringer/utils.rb
CHANGED
@@ -179,6 +179,7 @@ def read_from_minibuffer(prompt, completion_proc: nil, default: nil,
|
|
179
179
|
Window.current.buffer = Buffer.current = old_buffer
|
180
180
|
Buffer.minibuffer[:completion_proc] = old_completion_proc
|
181
181
|
Buffer.minibuffer.keymap = old_minibuffer_map
|
182
|
+
Buffer.minibuffer.disable_input_method
|
182
183
|
Controller.current.current_prefix_arg = old_current_prefix_arg
|
183
184
|
if COMPLETION[:original_buffer]
|
184
185
|
COMPLETION[:completions_window].buffer = COMPLETION[:original_buffer]
|
@@ -199,8 +200,9 @@ def read_file_name(prompt, default: nil)
|
|
199
200
|
end
|
200
201
|
}
|
201
202
|
}
|
203
|
+
initial_value = default&.sub(%r"\A#{Regexp.quote(Dir.pwd)}/", "")
|
202
204
|
file = read_from_minibuffer(prompt, completion_proc: f,
|
203
|
-
initial_value:
|
205
|
+
initial_value: initial_value)
|
204
206
|
File.expand_path(file)
|
205
207
|
end
|
206
208
|
|
data/lib/textbringer/version.rb
CHANGED
data/lib/textbringer/window.rb
CHANGED
@@ -43,24 +43,27 @@ def self.current=(window)
|
|
43
43
|
Buffer.current = window.buffer
|
44
44
|
end
|
45
45
|
|
46
|
-
def self.delete_window
|
47
|
-
if
|
46
|
+
def self.delete_window(target = @@current)
|
47
|
+
if target.echo_area?
|
48
48
|
raise EditorError, "Can't delete the echo area"
|
49
49
|
end
|
50
50
|
if @@list.size == 2
|
51
51
|
raise EditorError, "Can't delete the sole window"
|
52
52
|
end
|
53
|
-
i = @@list.index(
|
53
|
+
i = @@list.index(target)
|
54
|
+
return if i.nil?
|
54
55
|
if i == 0
|
55
56
|
window = @@list[1]
|
56
57
|
window.move(0, 0)
|
57
58
|
else
|
58
59
|
window = @@list[i - 1]
|
59
60
|
end
|
60
|
-
window.resize(
|
61
|
-
|
61
|
+
window.resize(target.lines + window.lines, window.columns)
|
62
|
+
target.delete
|
62
63
|
@@list.delete_at(i)
|
63
|
-
|
64
|
+
if target == @@current
|
65
|
+
self.current = window
|
66
|
+
end
|
64
67
|
end
|
65
68
|
|
66
69
|
def self.delete_other_windows
|
@@ -455,8 +458,9 @@ def redisplay
|
|
455
458
|
end
|
456
459
|
end
|
457
460
|
end
|
458
|
-
if c ==
|
459
|
-
# ncurses on macOS prints U+FEFF as space,
|
461
|
+
if Buffer.display_width(c) == 0
|
462
|
+
# ncurses on macOS prints U+FEFF, U+FE0F etc. as space,
|
463
|
+
# so ignore it
|
460
464
|
else
|
461
465
|
@window.addstr(c)
|
462
466
|
end
|
@@ -542,9 +546,16 @@ def scroll_down
|
|
542
546
|
@top_of_window.location = 0
|
543
547
|
end
|
544
548
|
|
545
|
-
def split
|
549
|
+
def split(other_lines = nil)
|
546
550
|
old_lines = lines
|
547
|
-
|
551
|
+
if other_lines
|
552
|
+
if other_lines < CONFIG[:window_min_height]
|
553
|
+
raise EditorError, "Window too small"
|
554
|
+
end
|
555
|
+
new_lines = lines - other_lines
|
556
|
+
else
|
557
|
+
new_lines = (old_lines / 2.0).ceil
|
558
|
+
end
|
548
559
|
if new_lines < CONFIG[:window_min_height]
|
549
560
|
raise EditorError, "Window too small"
|
550
561
|
end
|
@@ -649,7 +660,7 @@ def redisplay_mode_line
|
|
649
660
|
@mode_line.setpos(0, 0)
|
650
661
|
attrs = @@has_colors ? Face[:mode_line].attributes : Curses::A_REVERSE
|
651
662
|
@mode_line.attrset(attrs)
|
652
|
-
@mode_line.addstr("#{@buffer.name} ")
|
663
|
+
@mode_line.addstr("#{@buffer.input_method_status} #{@buffer.name} ")
|
653
664
|
@mode_line.addstr("[+]") if @buffer.modified?
|
654
665
|
@mode_line.addstr("[RO]") if @buffer.read_only?
|
655
666
|
@mode_line.addstr("[#{@buffer.file_encoding.name}/")
|
data/textbringer.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.required_ruby_version = '>= 2.
|
22
|
+
spec.required_ruby_version = '>= 2.6'
|
23
23
|
|
24
24
|
spec.add_runtime_dependency "curses", ">= 1.2.7"
|
25
25
|
spec.add_runtime_dependency "unicode-display_width", "~> 1.1"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: textbringer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: curses
|
@@ -187,6 +187,7 @@ files:
|
|
187
187
|
- README.md
|
188
188
|
- Rakefile
|
189
189
|
- bin/console
|
190
|
+
- bin/merge_mazegaki_dic
|
190
191
|
- exe/tbclient
|
191
192
|
- exe/tbtags
|
192
193
|
- exe/textbringer
|
@@ -201,6 +202,7 @@ files:
|
|
201
202
|
- lib/textbringer/commands/files.rb
|
202
203
|
- lib/textbringer/commands/fill.rb
|
203
204
|
- lib/textbringer/commands/help.rb
|
205
|
+
- lib/textbringer/commands/input_method.rb
|
204
206
|
- lib/textbringer/commands/isearch.rb
|
205
207
|
- lib/textbringer/commands/keyboard_macro.rb
|
206
208
|
- lib/textbringer/commands/misc.rb
|
@@ -214,6 +216,10 @@ files:
|
|
214
216
|
- lib/textbringer/face.rb
|
215
217
|
- lib/textbringer/faces/basic.rb
|
216
218
|
- lib/textbringer/faces/programming.rb
|
219
|
+
- lib/textbringer/input_method.rb
|
220
|
+
- lib/textbringer/input_methods/hiragana_input_method.rb
|
221
|
+
- lib/textbringer/input_methods/t_code_input_method.rb
|
222
|
+
- lib/textbringer/input_methods/t_code_input_method/tables.rb
|
217
223
|
- lib/textbringer/keymap.rb
|
218
224
|
- lib/textbringer/mode.rb
|
219
225
|
- lib/textbringer/modes/backtrace_mode.rb
|
@@ -245,14 +251,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
245
251
|
requirements:
|
246
252
|
- - ">="
|
247
253
|
- !ruby/object:Gem::Version
|
248
|
-
version: '2.
|
254
|
+
version: '2.6'
|
249
255
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
250
256
|
requirements:
|
251
257
|
- - ">="
|
252
258
|
- !ruby/object:Gem::Version
|
253
259
|
version: '0'
|
254
260
|
requirements: []
|
255
|
-
rubygems_version: 3.
|
261
|
+
rubygems_version: 3.3.0.dev
|
256
262
|
signing_key:
|
257
263
|
specification_version: 4
|
258
264
|
summary: An Emacs-like text editor
|