textbringer 10 → 12
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/dependabot.yml +6 -0
- data/.github/workflows/push_gem.yml +2 -2
- data/lib/textbringer/buffer.rb +184 -0
- data/lib/textbringer/commands/buffers.rb +34 -0
- data/lib/textbringer/commands/files.rb +9 -0
- data/lib/textbringer/commands/server.rb +52 -2
- data/lib/textbringer/keymap.rb +7 -0
- data/lib/textbringer/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04d0f8e23504a6d3ae1ce1ccc4cf1ca4c8308525cd61356d22e50631474408bc
|
4
|
+
data.tar.gz: a16eefeef6df621bf2272a4437b1321c999b7ffd3f0cf7f1353d0a41baabd7b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f03f8d34e41b099d3b9dcaecbb1fa40b3e9c9f3c43a7aba7917dcfe8a7cccbd1dec97599c46e657839af70d8abf67681f339d5ba97f6abb308e76fe1ffbd93f0
|
7
|
+
data.tar.gz: 643f15490cc3ce708c8b6aced38b1249b7c4074a6a5fcb9c7fd927ee1493778fdefd9c9f393a968630e89a85823e13deb036c481691651b02580b2bc150a1a14
|
@@ -24,7 +24,7 @@ jobs:
|
|
24
24
|
steps:
|
25
25
|
# Set up
|
26
26
|
- name: Harden Runner
|
27
|
-
uses: step-security/harden-runner@
|
27
|
+
uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2
|
28
28
|
with:
|
29
29
|
egress-policy: audit
|
30
30
|
|
@@ -50,5 +50,5 @@ jobs:
|
|
50
50
|
- name: Purge GitHub cache
|
51
51
|
shell: bash
|
52
52
|
run: |
|
53
|
-
urls=$(curl -
|
53
|
+
urls=$(curl -sL "https://github.com/${{ github.repository_owner }}/${{ github.event.repository.name }}/tree/main" | grep -Eo "(http|https)://camo.githubusercontent.com[a-zA-Z0-9./?=_%:-]*")
|
54
54
|
while IFS= read -r url; do echo -e "\nPurge $url"; curl -X PURGE "$url"; done <<< "$urls"
|
data/lib/textbringer/buffer.rb
CHANGED
@@ -313,6 +313,10 @@ module Textbringer
|
|
313
313
|
end
|
314
314
|
end
|
315
315
|
|
316
|
+
def set_read_only(value)
|
317
|
+
@read_only = value
|
318
|
+
end
|
319
|
+
|
316
320
|
def read_only_edit
|
317
321
|
self.read_only = false
|
318
322
|
begin
|
@@ -1085,6 +1089,185 @@ module Textbringer
|
|
1085
1089
|
insert_for_yank(KILL_RING.rotate(1))
|
1086
1090
|
end
|
1087
1091
|
|
1092
|
+
def rectangle_boundaries(s = @point, e = mark)
|
1093
|
+
s, e = Buffer.region_boundaries(s, e)
|
1094
|
+
save_excursion do
|
1095
|
+
goto_char(s)
|
1096
|
+
start_line = @current_line
|
1097
|
+
beginning_of_line
|
1098
|
+
start_col = display_width(substring(@point, s)) + 1
|
1099
|
+
goto_char(e)
|
1100
|
+
end_line = @current_line
|
1101
|
+
beginning_of_line
|
1102
|
+
end_col = display_width(substring(@point, e)) + 1
|
1103
|
+
|
1104
|
+
# Ensure start_col <= end_col
|
1105
|
+
if start_col > end_col
|
1106
|
+
start_col, end_col = end_col, start_col
|
1107
|
+
end
|
1108
|
+
[start_line, start_col, end_line, end_col]
|
1109
|
+
end
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
def extract_rectangle(s = @point, e = mark)
|
1113
|
+
start_line, start_col, end_line, end_col = rectangle_boundaries(s, e)
|
1114
|
+
lines = []
|
1115
|
+
rectangle_width = end_col - start_col
|
1116
|
+
|
1117
|
+
save_excursion do
|
1118
|
+
goto_line(start_line)
|
1119
|
+
(start_line..end_line).each do |line_num|
|
1120
|
+
beginning_of_line
|
1121
|
+
line_start = @point
|
1122
|
+
|
1123
|
+
# Move to start column
|
1124
|
+
col = 1
|
1125
|
+
while col < start_col && !end_of_line?
|
1126
|
+
forward_char
|
1127
|
+
col = 1 + display_width(substring(line_start, @point))
|
1128
|
+
end
|
1129
|
+
start_pos = @point
|
1130
|
+
|
1131
|
+
# If we haven't reached start_col, the line is too short
|
1132
|
+
if col < start_col
|
1133
|
+
# Line is shorter than start column, extract all spaces
|
1134
|
+
lines << " " * rectangle_width
|
1135
|
+
else
|
1136
|
+
# Move to end column
|
1137
|
+
while col < end_col && !end_of_line?
|
1138
|
+
forward_char
|
1139
|
+
col = 1 + display_width(substring(line_start, @point))
|
1140
|
+
end
|
1141
|
+
end_pos = @point
|
1142
|
+
|
1143
|
+
# Extract the rectangle text for this line
|
1144
|
+
if end_pos > start_pos
|
1145
|
+
extracted = substring(start_pos, end_pos)
|
1146
|
+
# Pad with spaces if the extracted text is shorter than rectangle width
|
1147
|
+
extracted_width = display_width(extracted)
|
1148
|
+
if extracted_width < rectangle_width
|
1149
|
+
extracted += " " * (rectangle_width - extracted_width)
|
1150
|
+
end
|
1151
|
+
lines << extracted
|
1152
|
+
else
|
1153
|
+
lines << " " * rectangle_width
|
1154
|
+
end
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
# Move to next line
|
1158
|
+
break if line_num == end_line
|
1159
|
+
forward_line
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
lines
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
def copy_rectangle(s = @point, e = mark)
|
1167
|
+
lines = extract_rectangle(s, e)
|
1168
|
+
@@killed_rectangle = lines
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
def kill_rectangle(s = @point, e = mark)
|
1172
|
+
copy_rectangle(s, e)
|
1173
|
+
delete_rectangle(s, e)
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
def delete_rectangle(s = @point, e = mark)
|
1177
|
+
check_read_only_flag
|
1178
|
+
start_line, start_col, end_line, end_col = rectangle_boundaries(s, e)
|
1179
|
+
|
1180
|
+
save_excursion do
|
1181
|
+
# Delete from bottom to top to avoid position shifts
|
1182
|
+
(start_line..end_line).reverse_each do |line_num|
|
1183
|
+
goto_line(line_num)
|
1184
|
+
beginning_of_line
|
1185
|
+
line_start = @point
|
1186
|
+
|
1187
|
+
# Move to start column
|
1188
|
+
col = 1
|
1189
|
+
while col < start_col && !end_of_line?
|
1190
|
+
forward_char
|
1191
|
+
col = 1 + display_width(substring(line_start, @point))
|
1192
|
+
end
|
1193
|
+
start_pos = @point
|
1194
|
+
|
1195
|
+
# Only delete if we're within the line bounds
|
1196
|
+
if col >= start_col
|
1197
|
+
# Move to end column
|
1198
|
+
while col < end_col && !end_of_line?
|
1199
|
+
forward_char
|
1200
|
+
col = 1 + display_width(substring(line_start, @point))
|
1201
|
+
end
|
1202
|
+
end_pos = @point
|
1203
|
+
|
1204
|
+
# Delete the rectangle text for this line
|
1205
|
+
if end_pos > start_pos
|
1206
|
+
delete_region(start_pos, end_pos)
|
1207
|
+
end
|
1208
|
+
end
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
def yank_rectangle
|
1214
|
+
raise "No rectangle in kill ring" if @@killed_rectangle.nil?
|
1215
|
+
lines = @@killed_rectangle
|
1216
|
+
start_line, start_col = get_line_and_column(@point)
|
1217
|
+
|
1218
|
+
save_excursion do
|
1219
|
+
lines.each_with_index do |line, i|
|
1220
|
+
goto_line(start_line + i)
|
1221
|
+
beginning_of_line
|
1222
|
+
line_start = @point
|
1223
|
+
|
1224
|
+
# Move to start column, extending line if necessary
|
1225
|
+
col = 1
|
1226
|
+
while col < start_col && !end_of_line?
|
1227
|
+
forward_char
|
1228
|
+
col = 1 + display_width(substring(line_start, @point))
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
# If line is shorter than start_col, extend it with spaces
|
1232
|
+
if col < start_col
|
1233
|
+
insert(" " * (start_col - col))
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
# Insert the rectangle line
|
1237
|
+
insert(line)
|
1238
|
+
end
|
1239
|
+
end
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
def open_rectangle(s = @point, e = mark)
|
1243
|
+
check_read_only_flag
|
1244
|
+
start_line, start_col, end_line, end_col = rectangle_boundaries(s, e)
|
1245
|
+
width = end_col - start_col
|
1246
|
+
|
1247
|
+
save_excursion do
|
1248
|
+
(start_line..end_line).each do |line_num|
|
1249
|
+
goto_line(line_num)
|
1250
|
+
beginning_of_line
|
1251
|
+
line_start = @point
|
1252
|
+
|
1253
|
+
# Move to start column, extending line if necessary
|
1254
|
+
col = 1
|
1255
|
+
while col < start_col && !end_of_line?
|
1256
|
+
forward_char
|
1257
|
+
col = 1 + display_width(substring(line_start, @point))
|
1258
|
+
end
|
1259
|
+
|
1260
|
+
# If line is shorter than start_col, extend it with spaces
|
1261
|
+
if col < start_col
|
1262
|
+
insert(" " * (start_col - col))
|
1263
|
+
end
|
1264
|
+
|
1265
|
+
# Insert spaces to create the rectangle
|
1266
|
+
insert(" " * width)
|
1267
|
+
end
|
1268
|
+
end
|
1269
|
+
end
|
1270
|
+
|
1088
1271
|
def undo
|
1089
1272
|
undo_or_redo(:undo, @undo_stack, @redo_stack)
|
1090
1273
|
end
|
@@ -1746,6 +1929,7 @@ module Textbringer
|
|
1746
1929
|
end
|
1747
1930
|
|
1748
1931
|
KILL_RING = Ring.new
|
1932
|
+
@@killed_rectangle = nil
|
1749
1933
|
|
1750
1934
|
class UndoableAction
|
1751
1935
|
attr_accessor :version
|
@@ -129,6 +129,31 @@ module Textbringer
|
|
129
129
|
Buffer.current.delete_region
|
130
130
|
end
|
131
131
|
|
132
|
+
define_command(:kill_rectangle,
|
133
|
+
doc: "Kill the text of the region-rectangle, saving its contents as the last killed rectangle.") do
|
134
|
+
Buffer.current.kill_rectangle
|
135
|
+
end
|
136
|
+
|
137
|
+
define_command(:copy_rectangle_as_kill,
|
138
|
+
doc: "Save the text of the region-rectangle as the last killed rectangle.") do
|
139
|
+
Buffer.current.copy_rectangle
|
140
|
+
end
|
141
|
+
|
142
|
+
define_command(:delete_rectangle,
|
143
|
+
doc: "Delete the text of the region-rectangle.") do
|
144
|
+
Buffer.current.delete_rectangle
|
145
|
+
end
|
146
|
+
|
147
|
+
define_command(:yank_rectangle,
|
148
|
+
doc: "Yank the last killed rectangle with its upper left corner at point.") do
|
149
|
+
Buffer.current.yank_rectangle
|
150
|
+
end
|
151
|
+
|
152
|
+
define_command(:open_rectangle,
|
153
|
+
doc: "Insert blank space to fill the space of the region-rectangle. This pushes the previous contents of the region-rectangle to the right.") do
|
154
|
+
Buffer.current.open_rectangle
|
155
|
+
end
|
156
|
+
|
132
157
|
define_command(:transpose_chars,
|
133
158
|
doc: "Transpose characters.") do
|
134
159
|
Buffer.current.transpose_chars
|
@@ -298,5 +323,14 @@ module Textbringer
|
|
298
323
|
n = number_prefix_arg|
|
299
324
|
Buffer.current.insert(c.hex.chr(Encoding::UTF_8) * n)
|
300
325
|
end
|
326
|
+
|
327
|
+
define_command(:read_only_mode,
|
328
|
+
doc: <<~EOD) do
|
329
|
+
Change whether the current buffer is read-only.
|
330
|
+
EOD
|
331
|
+
Buffer.current.set_read_only(!Buffer.current.read_only?)
|
332
|
+
status = Buffer.current.read_only? ? "enabled" : "disabled"
|
333
|
+
message("Read-only mode #{status} in current buffer")
|
334
|
+
end
|
301
335
|
end
|
302
336
|
end
|
@@ -131,6 +131,15 @@ module Textbringer
|
|
131
131
|
Dir.chdir(File.expand_path(dir_name))
|
132
132
|
end
|
133
133
|
|
134
|
+
define_command(:find_file_read_only, doc: "Open a file but don't allow changes.") do
|
135
|
+
|file_name = read_file_name("Find file: ", default: (Buffer.current.file_name ? File.dirname(Buffer.current.file_name) : Dir.pwd) + "/")|
|
136
|
+
if !File.exist?(file_name)
|
137
|
+
raise EditorError, "#{file_name} does not exist"
|
138
|
+
end
|
139
|
+
find_file(file_name)
|
140
|
+
read_only_mode
|
141
|
+
end
|
142
|
+
|
134
143
|
define_command(:find_alternate_file, doc: "Find an alternate file.") do
|
135
144
|
|file_name = read_file_name("Find alternate file: ",
|
136
145
|
default: Buffer.current.file_name)|
|
@@ -6,8 +6,8 @@ module Textbringer
|
|
6
6
|
doc: "Start Textbringer server.") do
|
7
7
|
uri = CONFIG[:server_uri] ||
|
8
8
|
"drbunix:" + File.expand_path("server.sock", "~/.textbringer")
|
9
|
-
|
10
|
-
|
9
|
+
server = Server.new(uri)
|
10
|
+
server.start
|
11
11
|
end
|
12
12
|
|
13
13
|
define_command(:server_kill,
|
@@ -31,6 +31,56 @@ module Textbringer
|
|
31
31
|
end
|
32
32
|
|
33
33
|
class Server
|
34
|
+
def initialize(uri)
|
35
|
+
@uri = uri
|
36
|
+
end
|
37
|
+
|
38
|
+
def start
|
39
|
+
front = FrontObject.new
|
40
|
+
default_options = unix_domain? ? { UNIXFileMode: 0600 } : {}
|
41
|
+
options = default_options.merge(CONFIG[:server_options] || {})
|
42
|
+
begin
|
43
|
+
DRb.start_service(@uri, front, options)
|
44
|
+
rescue Errno::EADDRINUSE
|
45
|
+
if unix_domain? && !unix_domain_server_alive?
|
46
|
+
# Remove the socket file in case another server died unexpectedly before.
|
47
|
+
File.unlink(unix_domain_socket_path)
|
48
|
+
DRb.start_service(@uri, front, options)
|
49
|
+
else
|
50
|
+
raise ExistError, "There is an existing Textbringer server"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def unix_domain?
|
58
|
+
@uri.start_with?("drbunix:")
|
59
|
+
end
|
60
|
+
|
61
|
+
def unix_domain_socket_path
|
62
|
+
@uri.sub(/\Adrbunix:/, "")
|
63
|
+
end
|
64
|
+
|
65
|
+
def unix_domain_server_alive?
|
66
|
+
socket = Socket.new(:UNIX, :STREAM)
|
67
|
+
sockaddr = Socket.sockaddr_un(unix_domain_socket_path)
|
68
|
+
begin
|
69
|
+
socket.connect_nonblock(sockaddr)
|
70
|
+
rescue Errno::EINPROGRESS
|
71
|
+
return true
|
72
|
+
rescue Errno::ECONNREFUSED
|
73
|
+
return false
|
74
|
+
ensure
|
75
|
+
socket.close
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Server::ExistError < EditorError
|
81
|
+
end
|
82
|
+
|
83
|
+
class Server::FrontObject
|
34
84
|
def eval(s)
|
35
85
|
with_redisplay do
|
36
86
|
Controller.current.instance_eval(s).inspect
|
data/lib/textbringer/keymap.rb
CHANGED
@@ -176,6 +176,7 @@ module Textbringer
|
|
176
176
|
GLOBAL_MAP.define_key("\C-x\C-c", :exit_textbringer)
|
177
177
|
GLOBAL_MAP.define_key("\C-z", :suspend_textbringer)
|
178
178
|
GLOBAL_MAP.define_key("\C-x\C-f", :find_file)
|
179
|
+
GLOBAL_MAP.define_key("\C-x\C-r", :find_file_read_only)
|
179
180
|
GLOBAL_MAP.define_key("\C-x\C-v", :find_alternate_file)
|
180
181
|
GLOBAL_MAP.define_key("\C-xb", :switch_to_buffer)
|
181
182
|
GLOBAL_MAP.define_key("\C-x\C-b", :list_buffers)
|
@@ -202,6 +203,11 @@ module Textbringer
|
|
202
203
|
GLOBAL_MAP.define_key("\C-xri", :insert_register)
|
203
204
|
GLOBAL_MAP.define_key("\C-xrn", :number_to_register)
|
204
205
|
GLOBAL_MAP.define_key("\C-xr+", :increment_register)
|
206
|
+
GLOBAL_MAP.define_key("\C-xrk", :kill_rectangle)
|
207
|
+
GLOBAL_MAP.define_key("\C-xr\M-w", :copy_rectangle_as_kill)
|
208
|
+
GLOBAL_MAP.define_key("\C-xrd", :delete_rectangle)
|
209
|
+
GLOBAL_MAP.define_key("\C-xry", :yank_rectangle)
|
210
|
+
GLOBAL_MAP.define_key("\C-xro", :open_rectangle)
|
205
211
|
GLOBAL_MAP.define_key("\C-x(", :start_keyboard_macro)
|
206
212
|
GLOBAL_MAP.define_key(:f3, :start_keyboard_macro)
|
207
213
|
GLOBAL_MAP.define_key("\C-x)", :end_keyboard_macro)
|
@@ -220,6 +226,7 @@ module Textbringer
|
|
220
226
|
GLOBAL_MAP.define_key("\M-c", :capitalize_word)
|
221
227
|
GLOBAL_MAP.define_key("\C-x8\C-m", :insert_char)
|
222
228
|
GLOBAL_MAP.define_key("\C-x=", :what_cursor_position)
|
229
|
+
GLOBAL_MAP.define_key("\C-x\C-q", :read_only_mode)
|
223
230
|
GLOBAL_MAP.define_key(:ic, :overwrite_mode)
|
224
231
|
GLOBAL_MAP.handle_undefined_key do |key|
|
225
232
|
if key.is_a?(String) && /[\0-\x7f]/ !~ key
|
data/lib/textbringer/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: textbringer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '
|
4
|
+
version: '12'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
@@ -330,6 +330,7 @@ extra_rdoc_files: []
|
|
330
330
|
files:
|
331
331
|
- ".editorconfig"
|
332
332
|
- ".gitattributes"
|
333
|
+
- ".github/dependabot.yml"
|
333
334
|
- ".github/workflows/macos.yml"
|
334
335
|
- ".github/workflows/push_gem.yml"
|
335
336
|
- ".github/workflows/ubuntu.yml"
|