textbringer 26 → 28
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.md +7 -1
- data/lib/textbringer/commands/clipboard.rb +7 -4
- data/lib/textbringer/commands/fill.rb +152 -59
- data/lib/textbringer/commands/misc.rb +30 -0
- data/lib/textbringer/face.rb +18 -0
- data/lib/textbringer/input_methods/skk_input_method.rb +710 -74
- data/lib/textbringer/keymap.rb +2 -0
- data/lib/textbringer/mode.rb +19 -0
- data/lib/textbringer/modes/c_mode.rb +6 -0
- data/lib/textbringer/modes/programming_mode.rb +3 -6
- data/lib/textbringer/modes/ruby_mode.rb +27 -11
- data/lib/textbringer/themes/iceberg.rb +88 -0
- data/lib/textbringer/themes/kanagawa.rb +94 -0
- data/lib/textbringer/version.rb +1 -1
- data/lib/textbringer/window.rb +4 -1
- data/textbringer.gemspec +1 -2
- metadata +14 -26
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a93c844e10f5ad02a288323d8090d5dedea44b7f6050b054f9adc100e2bac129
|
|
4
|
+
data.tar.gz: 6a54096a278fba823491914ea9b184f1c640d64f5234192dabf64afad7ad3ce1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f19c746e74c12884bd99d827faaf0eee55a2dca803c1a2518f857286c00c91b0d7ad7d3516ce3e7e659cabd32db841674d9f42158ce01381ad9ea0b0901d71f0
|
|
7
|
+
data.tar.gz: 9aaf8575f47fd4e20bcd9cd868cad9b232bc7444fc29311c68d844058ab620d94a732b84ade0b527ee1ba2122f2a50f42981d17353de5c78f2c51c4535f24cd9
|
data/README.md
CHANGED
|
@@ -31,6 +31,10 @@ Install ncursesw before installing curses.gem, on which textbringer depends.
|
|
|
31
31
|
$ sudo apt-get install libncursesw5-dev
|
|
32
32
|
$ gem install curses
|
|
33
33
|
|
|
34
|
+
You need ffi to use clipboard commands on Windows.
|
|
35
|
+
|
|
36
|
+
$ gem install ffi
|
|
37
|
+
|
|
34
38
|
## Usage
|
|
35
39
|
|
|
36
40
|
$ txtb
|
|
@@ -131,7 +135,9 @@ set_background_color "default"
|
|
|
131
135
|
|
|
132
136
|
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.
|
|
133
137
|
|
|
134
|
-
To
|
|
138
|
+
To run the tests, run `bundle exec rake test`. To install this gem onto your local machine, run `bundle exec rake install`.
|
|
139
|
+
|
|
140
|
+
To release a new version, run `bundle exec rake bump` on a clean `main` branch. It increments the version number in `version.rb`, commits and pushes the change, and pushes a `v<N>` tag. The tag triggers the `push_gem.yml` workflow, which publishes the gem to [rubygems.org](https://rubygems.org) via trusted publishing and creates a GitHub release. Do not run `bundle exec rake release`; it would try to push the gem from your machine as well.
|
|
135
141
|
|
|
136
142
|
## Contributing
|
|
137
143
|
|
|
@@ -2,12 +2,15 @@ module Clipboard
|
|
|
2
2
|
@implementation = nil
|
|
3
3
|
end
|
|
4
4
|
|
|
5
|
-
require "clipboard"
|
|
6
|
-
|
|
7
5
|
module Textbringer
|
|
8
6
|
module Commands
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
begin
|
|
8
|
+
require "clipboard"
|
|
9
|
+
CLIPBOARD_AVAILABLE =
|
|
10
|
+
Clipboard.implementation.name != "Clipboard::File"
|
|
11
|
+
rescue LoadError
|
|
12
|
+
CLIPBOARD_AVAILABLE = false
|
|
13
|
+
end
|
|
11
14
|
|
|
12
15
|
if CLIPBOARD_AVAILABLE
|
|
13
16
|
GLOBAL_MAP.define_key("\M-w", :clipboard_copy_region)
|
|
@@ -2,32 +2,151 @@ require "stringio"
|
|
|
2
2
|
|
|
3
3
|
module Textbringer
|
|
4
4
|
module FillExtension
|
|
5
|
+
class Filler
|
|
6
|
+
GRAPH = /(?=[\u{0000}-\u{00FF}])[[:graph:]]/
|
|
7
|
+
|
|
8
|
+
def initialize(column, prefix)
|
|
9
|
+
@column = column
|
|
10
|
+
@prefix = prefix
|
|
11
|
+
@prefix_width = Buffer.display_width(prefix)
|
|
12
|
+
@fill_column = CONFIG[:fill_column]
|
|
13
|
+
@output = +""
|
|
14
|
+
@line_start = 0
|
|
15
|
+
@prev_c = nil
|
|
16
|
+
@bol = column == 0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# head is kept as it is at the start of the first line; body is the
|
|
20
|
+
# text to fill, with the prefix already removed from each line.
|
|
21
|
+
def fill(head, body)
|
|
22
|
+
if !head.empty?
|
|
23
|
+
@output << head
|
|
24
|
+
@line_start = head.size
|
|
25
|
+
@column += Buffer.display_width(head)
|
|
26
|
+
@bol = false
|
|
27
|
+
end
|
|
28
|
+
input = StringIO.new(body)
|
|
29
|
+
while c = input.getc
|
|
30
|
+
if c == "\n"
|
|
31
|
+
skip_blanks(input)
|
|
32
|
+
if input.eof?
|
|
33
|
+
insert_final_newline
|
|
34
|
+
elsif @column < @fill_column
|
|
35
|
+
if GRAPH.match?(@prev_c)
|
|
36
|
+
insert_space_between_words(input)
|
|
37
|
+
end
|
|
38
|
+
next
|
|
39
|
+
else
|
|
40
|
+
insert_newline(@prefix)
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
w = Buffer.display_width(c)
|
|
44
|
+
if @column + w > @fill_column || @column >= @fill_column
|
|
45
|
+
if /\w/.match?(@prev_c) && /\w/.match?(c)
|
|
46
|
+
insert_newline_before_word
|
|
47
|
+
else
|
|
48
|
+
insert_newline(@prefix)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
insert_char(c, w)
|
|
52
|
+
end
|
|
53
|
+
@prev_c = c
|
|
54
|
+
end
|
|
55
|
+
@output
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def skip_blanks(input)
|
|
61
|
+
while c = input.getc
|
|
62
|
+
if !/[ \t]/.match?(c)
|
|
63
|
+
input.ungetc(c)
|
|
64
|
+
break
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def insert_space_between_words(input)
|
|
70
|
+
c = input.getc
|
|
71
|
+
input.ungetc(c)
|
|
72
|
+
if GRAPH.match?(c)
|
|
73
|
+
@output << " "
|
|
74
|
+
@column += 1
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def insert_newline_before_word
|
|
79
|
+
m = @output.match(/(?:([^\w \t\n])|(\w)[ \t]+)(\w*)\z/, @line_start)
|
|
80
|
+
return if m.nil?
|
|
81
|
+
word = m[3]
|
|
82
|
+
@output[m.begin(0)..] = "#{m[1]}#{m[2]}\n#{@prefix}#{word}"
|
|
83
|
+
@line_start = @output.size - word.size
|
|
84
|
+
@column = @prefix_width + Buffer.display_width(word)
|
|
85
|
+
@bol = false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def insert_newline(prefix)
|
|
89
|
+
return if @bol
|
|
90
|
+
@output.sub!(/[ \t]+\z/, "")
|
|
91
|
+
@output << "\n" << prefix
|
|
92
|
+
@line_start = @output.size
|
|
93
|
+
@column = Buffer.display_width(prefix)
|
|
94
|
+
@bol = true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The newline that ends the text is kept even on an otherwise empty
|
|
98
|
+
# line, and no prefix follows it.
|
|
99
|
+
def insert_final_newline
|
|
100
|
+
@output.sub!(/[ \t]+\z/, "")
|
|
101
|
+
@output << "\n"
|
|
102
|
+
@line_start = @output.size
|
|
103
|
+
@column = 0
|
|
104
|
+
@bol = true
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def insert_char(c, w)
|
|
108
|
+
if @bol && /[ \t]/.match?(c)
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
@output << c
|
|
112
|
+
@column += w
|
|
113
|
+
@bol = false
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
5
117
|
refine Buffer do
|
|
6
118
|
def fill_region(s = Buffer.current.point, e = Buffer.current.mark)
|
|
7
119
|
s, e = Buffer.region_boundaries(s, e)
|
|
8
120
|
save_excursion do
|
|
9
|
-
str = substring(s, e)
|
|
10
121
|
goto_char(s)
|
|
11
122
|
pos = point
|
|
12
123
|
beginning_of_line
|
|
13
124
|
column = Buffer.display_width(substring(point, pos))
|
|
14
|
-
|
|
125
|
+
prefix_re = fill_prefix_regexp(comment_line?)
|
|
126
|
+
prefix = fill_prefix(substring(point, e), prefix_re)
|
|
127
|
+
lines = substring(s, e).lines
|
|
128
|
+
head = column == 0 && lines[0] ? lines[0][prefix_re] : ""
|
|
129
|
+
body = lines.each_with_index.map { |line, i|
|
|
130
|
+
i == 0 ? line[head.size..] : line.sub(prefix_re, "")
|
|
131
|
+
}.join
|
|
132
|
+
replace(Filler.new(column, prefix).fill(head, body),
|
|
133
|
+
start: s, end: e)
|
|
15
134
|
end
|
|
16
135
|
end
|
|
17
136
|
|
|
18
137
|
def fill_paragraph
|
|
19
138
|
beginning_of_line
|
|
20
|
-
|
|
21
|
-
|
|
139
|
+
separator = fill_paragraph_separator(comment_line?)
|
|
140
|
+
while !beginning_of_buffer? && !looking_at?(separator)
|
|
22
141
|
backward_line
|
|
23
142
|
end
|
|
24
|
-
while looking_at?(
|
|
143
|
+
while !end_of_buffer? && looking_at?(separator)
|
|
25
144
|
forward_line
|
|
26
145
|
end
|
|
27
146
|
s = point
|
|
28
147
|
begin
|
|
29
148
|
forward_line
|
|
30
|
-
end while !end_of_buffer? && !looking_at?(
|
|
149
|
+
end while !end_of_buffer? && !looking_at?(separator)
|
|
31
150
|
if beginning_of_line?
|
|
32
151
|
backward_char
|
|
33
152
|
end
|
|
@@ -36,69 +155,43 @@ module Textbringer
|
|
|
36
155
|
|
|
37
156
|
private
|
|
38
157
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
while c = input.getc
|
|
45
|
-
if c == "\n"
|
|
46
|
-
if column < fill_column && !input.eof?
|
|
47
|
-
if /(?=[\u{0000}-\u{00FF}])[[:graph:]]/.match?(prev_c)
|
|
48
|
-
column = insert_space_between_words(input, output, column)
|
|
49
|
-
end
|
|
50
|
-
next
|
|
51
|
-
end
|
|
52
|
-
column = insert_newline(output)
|
|
53
|
-
else
|
|
54
|
-
w = Buffer.display_width(c)
|
|
55
|
-
if column + w > fill_column || column >= fill_column
|
|
56
|
-
if /\w/.match?(prev_c) && /\w/.match?(c)
|
|
57
|
-
column = insert_newline_before_word(output, column)
|
|
58
|
-
else
|
|
59
|
-
column = insert_newline(output)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
column = insert_char(output, column, c, w)
|
|
63
|
-
end
|
|
64
|
-
prev_c = c
|
|
65
|
-
end
|
|
66
|
-
output
|
|
158
|
+
# Whether the current line is a comment line of the buffer's mode.
|
|
159
|
+
# Point must be at the beginning of the line.
|
|
160
|
+
def comment_line?
|
|
161
|
+
re = mode&.comment_prefix_regexp
|
|
162
|
+
!re.nil? && looking_at?(re)
|
|
67
163
|
end
|
|
68
164
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if
|
|
73
|
-
|
|
74
|
-
column + 1
|
|
165
|
+
# A line that ends the paragraph: a blank line, or, when filling a
|
|
166
|
+
# comment, any line that is not a comment with something in it.
|
|
167
|
+
def fill_paragraph_separator(comment)
|
|
168
|
+
if comment
|
|
169
|
+
/^(?!#{mode.comment_prefix_regexp}[^ \t\n])/
|
|
75
170
|
else
|
|
76
|
-
|
|
171
|
+
/^[ \t]*$/
|
|
77
172
|
end
|
|
78
173
|
end
|
|
79
174
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
175
|
+
# What is stripped from the start of each line before filling and
|
|
176
|
+
# put back by the prefix: indentation, and the comment leader when
|
|
177
|
+
# filling a comment.
|
|
178
|
+
def fill_prefix_regexp(comment)
|
|
179
|
+
if comment
|
|
180
|
+
/\A(?:#{mode.comment_prefix_regexp}|[ \t]*)/
|
|
84
181
|
else
|
|
85
|
-
|
|
182
|
+
/\A[ \t]*/
|
|
86
183
|
end
|
|
87
184
|
end
|
|
88
185
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
else
|
|
99
|
-
output << c
|
|
100
|
-
column + w
|
|
101
|
-
end
|
|
186
|
+
# The prefix of the second line, or of the first line when there is
|
|
187
|
+
# no second line, is the prefix of every line after the first, as
|
|
188
|
+
# with adaptive-fill-mode in Emacs. The first line keeps its own
|
|
189
|
+
# prefix.
|
|
190
|
+
def fill_prefix(str, prefix_re)
|
|
191
|
+
first, second = str.lines
|
|
192
|
+
line = second && !second.sub(prefix_re, "").match?(/\A\n?\z/) ?
|
|
193
|
+
second : first
|
|
194
|
+
line ? line[prefix_re] : ""
|
|
102
195
|
end
|
|
103
196
|
end
|
|
104
197
|
end
|
|
@@ -354,6 +354,36 @@ module Textbringer
|
|
|
354
354
|
RubyVM::MJIT.resume
|
|
355
355
|
end
|
|
356
356
|
|
|
357
|
+
define_command(:forward_page,
|
|
358
|
+
doc: "Move forward to page boundary.") do
|
|
359
|
+
|n = number_prefix_arg|
|
|
360
|
+
buffer = Buffer.current
|
|
361
|
+
n.times do
|
|
362
|
+
unless buffer.re_search_forward(/^\f/, raise_error: false)
|
|
363
|
+
buffer.end_of_buffer
|
|
364
|
+
break
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
define_command(:backward_page,
|
|
370
|
+
doc: "Move backward to page boundary.") do
|
|
371
|
+
|n = number_prefix_arg|
|
|
372
|
+
buffer = Buffer.current
|
|
373
|
+
n.times do
|
|
374
|
+
if buffer.point == 0
|
|
375
|
+
break
|
|
376
|
+
end
|
|
377
|
+
buffer.backward_char
|
|
378
|
+
if buffer.re_search_backward(/^\f/, raise_error: false)
|
|
379
|
+
buffer.forward_char
|
|
380
|
+
else
|
|
381
|
+
buffer.beginning_of_buffer
|
|
382
|
+
break
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
357
387
|
define_command(:what_cursor_position,
|
|
358
388
|
doc: "Print info on cursor position.") do
|
|
359
389
|
|arg = current_prefix_arg|
|
data/lib/textbringer/face.rb
CHANGED
|
@@ -7,6 +7,8 @@ module Textbringer
|
|
|
7
7
|
@@face_table = {}
|
|
8
8
|
@@next_color_pair = 1
|
|
9
9
|
@@color_pair_cache = {}
|
|
10
|
+
@@default_foreground = -1
|
|
11
|
+
@@default_background = -1
|
|
10
12
|
|
|
11
13
|
def self.[](name)
|
|
12
14
|
@@face_table[name]
|
|
@@ -26,6 +28,20 @@ module Textbringer
|
|
|
26
28
|
@@face_table.delete(name)
|
|
27
29
|
end
|
|
28
30
|
|
|
31
|
+
# Set the concrete color numbers substituted for -1 (default color).
|
|
32
|
+
# PDCurses resolves -1 when init_pair is called and
|
|
33
|
+
# assume_default_colors only affects color pair 0, so faces must be
|
|
34
|
+
# re-resolved with concrete colors to get the new default colors.
|
|
35
|
+
def self.set_default_color_numbers(fg_num, bg_num)
|
|
36
|
+
return if fg_num == @@default_foreground &&
|
|
37
|
+
bg_num == @@default_background
|
|
38
|
+
@@default_foreground = fg_num
|
|
39
|
+
@@default_background = bg_num
|
|
40
|
+
@@face_table.each_value do |face|
|
|
41
|
+
face.send(:resolve_inheritance)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
29
45
|
def initialize(name, **opts)
|
|
30
46
|
@name = name
|
|
31
47
|
update(**opts)
|
|
@@ -68,6 +84,8 @@ module Textbringer
|
|
|
68
84
|
@reverse = @explicit_reverse.nil? ? (parent&.instance_variable_get(:@reverse) || false) : @explicit_reverse
|
|
69
85
|
fg_num = Color[@foreground]
|
|
70
86
|
bg_num = Color[@background]
|
|
87
|
+
fg_num = @@default_foreground if fg_num == -1
|
|
88
|
+
bg_num = @@default_background if bg_num == -1
|
|
71
89
|
key = [fg_num, bg_num]
|
|
72
90
|
unless @@color_pair_cache.key?(key)
|
|
73
91
|
@@color_pair_cache[key] = @@next_color_pair
|