textbringer 11 → 13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a4d3643869a923b01642fd4faecfdf36a530822193049c6d05458bd62afeb94
4
- data.tar.gz: 5e04d0bcfbfc5e20143ca5fe06669001f563eede4ae75c52b6200101dcc4be7d
3
+ metadata.gz: 2ebcccec1dcc4c9b2116dc02f0e4548d30f019c0da7fc1d0573f84cf08b8590d
4
+ data.tar.gz: 1f2b135717e1fde5ca8f13fdb9a967d5a7cad5aea6af83c040f35bec85c3bf23
5
5
  SHA512:
6
- metadata.gz: 4001fb9d0e71fce380ab475782730bac8ca3a4bf663dd70b1d86d9292b83c3228ecf63632d7359b80408e248c1a413131a30fbc3ff2a3d47cece701bffb3bfb0
7
- data.tar.gz: 02cd15438c13cdb52bd5e6f34199aa12f8840349f71ad33b6e61fdeefad543a41b9d9167ce02439bda78605dce6d290e1c95f27563c8647094547d4ef8a20704
6
+ metadata.gz: a9a9ea8e0234c2fda07af756eb12e8acc53a7f0c3e9cee57276adb742bda13a896875f4b21334794737a55d6e7c7c07168baea74304336e687834f3e5ba4b5d2
7
+ data.tar.gz: 1c3a84fc349f0d68cd55498d6ae45d159f672b824c8307e89e30bd4eb68c70126de3c358a9255177c74230b747b6255dbdfd36182fe9d56b34927b8a88f60fbb
@@ -550,6 +550,7 @@ module Textbringer
550
550
  end
551
551
 
552
552
  def goto_char(pos)
553
+ return pos if pos == @point
553
554
  if pos < 0 || pos > size
554
555
  raise RangeError, "Out of buffer"
555
556
  end
@@ -1089,6 +1090,203 @@ module Textbringer
1089
1090
  insert_for_yank(KILL_RING.rotate(1))
1090
1091
  end
1091
1092
 
1093
+ # Returns start_line, start_col, end_line, and end_col of the rectangle region
1094
+ # Note that start_col and end_col are 0-origin and width-based (neither 1-origin nor codepoint-based)
1095
+ def rectangle_boundaries(s = @point, e = mark)
1096
+ s, e = Buffer.region_boundaries(s, e)
1097
+ save_excursion do
1098
+ goto_char(s)
1099
+ start_line = @current_line
1100
+ beginning_of_line
1101
+ start_col = display_width(substring(@point, s))
1102
+ goto_char(e)
1103
+ end_line = @current_line
1104
+ beginning_of_line
1105
+ end_col = display_width(substring(@point, e))
1106
+
1107
+ # Ensure start_col <= end_col
1108
+ if start_col > end_col
1109
+ start_col, end_col = end_col, start_col
1110
+ end
1111
+ [start_line, start_col, end_line, end_col]
1112
+ end
1113
+ end
1114
+
1115
+ def apply_on_rectangle(s = @point, e = mark, reverse: false)
1116
+ start_line, start_col, end_line, end_col = rectangle_boundaries(s, e)
1117
+
1118
+ save_excursion do
1119
+ composite_edit do
1120
+ if reverse
1121
+ goto_line(end_line)
1122
+ else
1123
+ goto_line(start_line)
1124
+ end
1125
+
1126
+ loop do
1127
+ beginning_of_line
1128
+ line_start = @point
1129
+
1130
+ # Move to start column
1131
+ col = 0
1132
+ while col < start_col && !end_of_line?
1133
+ forward_char
1134
+ col = display_width(substring(line_start, @point))
1135
+ end
1136
+
1137
+ yield(start_col, end_col, col, line_start)
1138
+
1139
+ # Move to next line for forward iteration
1140
+ if reverse
1141
+ break if @current_line <= start_line
1142
+ backward_line
1143
+ else
1144
+ break if @current_line >= end_line
1145
+ forward_line
1146
+ end
1147
+ end
1148
+ end
1149
+ end
1150
+ end
1151
+
1152
+ def extract_rectangle(s = @point, e = mark)
1153
+ lines = []
1154
+ apply_on_rectangle(s, e) do |start_col, end_col, col, line_start|
1155
+ start_pos = @point
1156
+ width = end_col - start_col
1157
+
1158
+ # If we haven't reached start_col, the line is too short
1159
+ if col < start_col
1160
+ # Line is shorter than start column, extract all spaces
1161
+ lines << " " * width
1162
+ else
1163
+ # Move to end column
1164
+ while col < end_col && !end_of_line?
1165
+ forward_char
1166
+ col = display_width(substring(line_start, @point))
1167
+ end
1168
+ end_pos = @point
1169
+
1170
+ # Extract the rectangle text for this line
1171
+ if end_pos > start_pos
1172
+ extracted = substring(start_pos, end_pos)
1173
+ # Pad with spaces if the extracted text is shorter than rectangle width
1174
+ extracted_width = display_width(extracted)
1175
+ if extracted_width < width
1176
+ extracted += " " * (width - extracted_width)
1177
+ end
1178
+ lines << extracted
1179
+ else
1180
+ lines << " " * width
1181
+ end
1182
+ end
1183
+ end
1184
+
1185
+ lines
1186
+ end
1187
+
1188
+ def copy_rectangle(s = @point, e = mark)
1189
+ lines = extract_rectangle(s, e)
1190
+ @@killed_rectangle = lines
1191
+ end
1192
+
1193
+ def kill_rectangle(s = @point, e = mark)
1194
+ copy_rectangle(s, e)
1195
+ delete_rectangle(s, e)
1196
+ end
1197
+
1198
+ def delete_rectangle(s = @point, e = mark)
1199
+ check_read_only_flag
1200
+
1201
+ apply_on_rectangle(s, e, reverse: true) do |start_col, end_col, col, line_start|
1202
+ start_pos = @point
1203
+
1204
+ # Only delete if we're within the line bounds
1205
+ if col >= start_col
1206
+ # Move to end column
1207
+ while col < end_col && !end_of_line?
1208
+ forward_char
1209
+ col = display_width(substring(line_start, @point))
1210
+ end
1211
+ end_pos = @point
1212
+
1213
+ # Delete the rectangle text for this line
1214
+ if end_pos > start_pos
1215
+ delete_region(start_pos, end_pos)
1216
+ end
1217
+ end
1218
+ end
1219
+ end
1220
+
1221
+ def yank_rectangle
1222
+ raise "No rectangle in kill ring" if @@killed_rectangle.nil?
1223
+ lines = @@killed_rectangle
1224
+ start_line = @current_line
1225
+ start_point = @point
1226
+ start_col = save_excursion {
1227
+ beginning_of_line
1228
+ display_width(substring(@point, start_point))
1229
+ }
1230
+ composite_edit do
1231
+ lines.each_with_index do |line, i|
1232
+ goto_line(start_line + i)
1233
+ beginning_of_line
1234
+ line_start = @point
1235
+
1236
+ # Move to start column, extending line if necessary
1237
+ col = 0
1238
+ while col < start_col && !end_of_line?
1239
+ forward_char
1240
+ col = display_width(substring(line_start, @point))
1241
+ end
1242
+
1243
+ # If line is shorter than start_col, extend it with spaces
1244
+ if col < start_col
1245
+ insert(" " * (start_col - col))
1246
+ end
1247
+
1248
+ # Insert the rectangle line
1249
+ insert(line)
1250
+ end
1251
+ end
1252
+ end
1253
+
1254
+ def open_rectangle(s = @point, e = mark)
1255
+ check_read_only_flag
1256
+ s, e = Buffer.region_boundaries(s, e)
1257
+ composite_edit do
1258
+ apply_on_rectangle(s, e) do |start_col, end_col, col, line_start|
1259
+ # If line is shorter than start_col, extend it with spaces
1260
+ if col < start_col
1261
+ insert(" " * (start_col - col))
1262
+ end
1263
+
1264
+ # Insert spaces to create the rectangle
1265
+ insert(" " * (end_col - start_col))
1266
+ end
1267
+ goto_char(s)
1268
+ end
1269
+ end
1270
+
1271
+ def clear_rectangle(s = @point, e = mark)
1272
+ check_read_only_flag
1273
+ apply_on_rectangle(s, e, reverse: true) do |start_col, end_col, col, line_start|
1274
+ start_pos = @point
1275
+ if col < start_col
1276
+ insert(" " * (end_col - start_col))
1277
+ else
1278
+ while col < end_col && !end_of_line?
1279
+ forward_char
1280
+ col = display_width(substring(line_start, @point))
1281
+ end
1282
+ end_pos = @point
1283
+
1284
+ delete_region(start_pos, end_pos) if end_pos > start_pos
1285
+ insert(" " * (end_col - start_col))
1286
+ end
1287
+ end
1288
+ end
1289
+
1092
1290
  def undo
1093
1291
  undo_or_redo(:undo, @undo_stack, @redo_stack)
1094
1292
  end
@@ -1256,14 +1454,14 @@ module Textbringer
1256
1454
  end
1257
1455
 
1258
1456
  def composite_edit
1457
+ location = @point
1259
1458
  @composite_edit_level += 1
1260
1459
  begin
1261
1460
  yield
1262
1461
  ensure
1263
1462
  @composite_edit_level -= 1
1264
1463
  if @composite_edit_level == 0 && !@composite_edit_actions.empty?
1265
- action = CompositeAction.new(self,
1266
- @composite_edit_actions.first.location)
1464
+ action = CompositeAction.new(self, location)
1267
1465
  @composite_edit_actions.each do |i|
1268
1466
  action.add_action(i)
1269
1467
  end
@@ -1750,6 +1948,7 @@ module Textbringer
1750
1948
  end
1751
1949
 
1752
1950
  KILL_RING = Ring.new
1951
+ @@killed_rectangle = nil
1753
1952
 
1754
1953
  class UndoableAction
1755
1954
  attr_accessor :version
@@ -1826,6 +2025,7 @@ module Textbringer
1826
2025
  @actions.reverse_each do |action|
1827
2026
  action.undo
1828
2027
  end
2028
+ @buffer.goto_char(@location)
1829
2029
  end
1830
2030
 
1831
2031
  def redo
@@ -129,6 +129,36 @@ 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
+
157
+ define_command(:clear_rectangle,
158
+ doc: "Clear the region-rectangle by replacing its contents with spaces.") do
159
+ Buffer.current.clear_rectangle
160
+ end
161
+
132
162
  define_command(:transpose_chars,
133
163
  doc: "Transpose characters.") do
134
164
  Buffer.current.transpose_chars
@@ -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
- options = { UNIXFileMode: 0600 }.merge(CONFIG[:server_options] || {})
10
- DRb.start_service(uri, Server.new, options)
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
@@ -203,6 +203,12 @@ module Textbringer
203
203
  GLOBAL_MAP.define_key("\C-xri", :insert_register)
204
204
  GLOBAL_MAP.define_key("\C-xrn", :number_to_register)
205
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)
211
+ GLOBAL_MAP.define_key("\C-xrc", :clear_rectangle)
206
212
  GLOBAL_MAP.define_key("\C-x(", :start_keyboard_macro)
207
213
  GLOBAL_MAP.define_key(:f3, :start_keyboard_macro)
208
214
  GLOBAL_MAP.define_key("\C-x)", :end_keyboard_macro)
@@ -1,3 +1,3 @@
1
1
  module Textbringer
2
- VERSION = "11"
2
+ VERSION = "13"
3
3
  end
data/textbringer.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'textbringer/version'
4
+ require_relative 'lib/textbringer/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "textbringer"
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/shugo/textbringer"
15
15
  spec.license = "MIT"
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^((test|spec|features|logo)/|\.)}) }
18
18
  spec.bindir = "exe"
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
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: '11'
4
+ version: '13'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
@@ -328,13 +328,6 @@ executables:
328
328
  extensions: []
329
329
  extra_rdoc_files: []
330
330
  files:
331
- - ".editorconfig"
332
- - ".gitattributes"
333
- - ".github/workflows/macos.yml"
334
- - ".github/workflows/push_gem.yml"
335
- - ".github/workflows/ubuntu.yml"
336
- - ".github/workflows/windows.yml"
337
- - ".gitignore"
338
331
  - CHANGES.md
339
332
  - Gemfile
340
333
  - LICENSE.txt
@@ -396,8 +389,6 @@ files:
396
389
  - lib/textbringer/version.rb
397
390
  - lib/textbringer/window.rb
398
391
  - lib/textbringer/window/fallback_characters.rb
399
- - logo/logo.jpg
400
- - logo/logo.png
401
392
  - screenshot.png
402
393
  - textbringer.gemspec
403
394
  homepage: https://github.com/shugo/textbringer
data/.editorconfig DELETED
@@ -1,12 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- end_of_line = lf
5
-
6
- [*.rb]
7
- indent_style = space
8
- indent_size = 2
9
-
10
- [lib/**.rb]
11
- trim_trailing_whitespace = true
12
- insert_final_newline = true
data/.gitattributes DELETED
@@ -1 +0,0 @@
1
- *.rb diff=ruby
@@ -1,23 +0,0 @@
1
- name: macos
2
-
3
- on: [push]
4
-
5
- jobs:
6
- test:
7
- runs-on: macos-latest
8
- strategy:
9
- matrix:
10
- ruby: [ head, 3.4 ]
11
- timeout-minutes: 10
12
- env:
13
- RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
14
- steps:
15
- - uses: actions/checkout@v4
16
- - uses: ruby/setup-ruby@v1
17
- with:
18
- ruby-version: ${{ matrix.ruby }}
19
- - name: Install dependencies
20
- run: |
21
- bundle install
22
- - name: Run test
23
- run: bundle exec rake test
@@ -1,54 +0,0 @@
1
- name: Publish gem to rubygems.org
2
-
3
- on:
4
- push:
5
- tags:
6
- - 'v*'
7
-
8
- permissions:
9
- contents: read
10
-
11
- jobs:
12
- push:
13
- if: github.repository == 'shugo/textbringer'
14
- runs-on: ubuntu-latest
15
-
16
- environment:
17
- name: rubygems.org
18
- url: https://rubygems.org/gems/textbringer
19
-
20
- permissions:
21
- contents: write
22
- id-token: write
23
-
24
- steps:
25
- # Set up
26
- - name: Harden Runner
27
- uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
28
- with:
29
- egress-policy: audit
30
-
31
- - uses: actions/checkout@v4
32
-
33
- - name: Set up Ruby
34
- uses: ruby/setup-ruby@v1
35
- with:
36
- bundler-cache: true
37
- ruby-version: ruby
38
-
39
- # Release
40
- - name: Publish to RubyGems
41
- uses: rubygems/release-gem@v1
42
-
43
- - name: Create GitHub release
44
- run: |
45
- tag_name="$(git describe --tags --abbrev=0)"
46
- gh release create "${tag_name}" --verify-tag --generate-notes
47
- env:
48
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49
-
50
- - name: Purge GitHub cache
51
- shell: bash
52
- run: |
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
- while IFS= read -r url; do echo -e "\nPurge $url"; curl -X PURGE "$url"; done <<< "$urls"
@@ -1,24 +0,0 @@
1
- name: ubuntu
2
-
3
- on: [push]
4
-
5
- jobs:
6
- test:
7
- runs-on: ubuntu-latest
8
- strategy:
9
- matrix:
10
- ruby: [ head, 3.4, 3.3, 3.2 ]
11
- timeout-minutes: 10
12
- env:
13
- RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
14
- steps:
15
- - uses: actions/checkout@v4
16
- - uses: ruby/setup-ruby@v1
17
- with:
18
- ruby-version: ${{ matrix.ruby }}
19
- - name: Install dependencies
20
- run: |
21
- sudo apt install libncursesw5-dev
22
- bundle install
23
- - name: Run test
24
- run: xvfb-run bundle exec rake test
@@ -1,22 +0,0 @@
1
- name: windows
2
-
3
- on: [push]
4
-
5
- jobs:
6
- test:
7
- runs-on: windows-latest
8
- strategy:
9
- matrix:
10
- ruby: [ '3.4' ]
11
- timeout-minutes: 10
12
- env:
13
- RUBYOPT: --enable-frozen-string-literal --debug-frozen-string-literal
14
- steps:
15
- - uses: actions/checkout@v4
16
- - name: Set up Ruby
17
- uses: ruby/setup-ruby@v1
18
- with:
19
- ruby-version: ${{ matrix.ruby }}
20
- bundler-cache: true
21
- - name: Run test
22
- run: bundle exec rake test
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- /tags
data/logo/logo.jpg DELETED
Binary file
data/logo/logo.png DELETED
Binary file