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