tarazed 0.2.5 → 0.2.7

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: 239db90ea477884f4eadb45190f4ab471475aea19341781edadd3324282c57b7
4
- data.tar.gz: 0516a4a738f05353a0165cafc2e7a6f25122844d1f3b93a9eaf0b58fd963a5d4
3
+ metadata.gz: edb5f9868496d45a99573a1ae693aca013d7d40240af9f87fdaa20d23e390f24
4
+ data.tar.gz: 789f6f2f36a48caa960b11d304a5e1188e14bfeb09a9dd61d4199a61877e030c
5
5
  SHA512:
6
- metadata.gz: 5b3cbd7d5bfb263316054117e092fa04963021e74379ce61fec5a88230733714d7e91e34a533f5606b80f443267591d11c7b08b08ea66baf567f1100452ca99e
7
- data.tar.gz: 70b84b619caacd1be2b1162cdc7e5c76cbc22b18af611643de938995c55d85030ff59b904627b2ddea2852f3dec77265c2ccd27ae14e0cc27e3ce9776fb875b5
6
+ metadata.gz: 82bc214aeee5727b48b0f69732fb0313aa2d95080ce0a14e2f4a6cf32507aade915af1ad7e3c15055419322c3372cede1a52c1d9d15379140ca9344fbed37da5
7
+ data.tar.gz: c1157de1a6ce563eb663160342044f110360221e90dcb4de7644d277772798e593a3bad191b4d2ae03fd6fd7b0aefcc4fe364b45bb46b0ea2fd1f27d7eb27808
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.2.7 - 2026-09-21
6
+
7
+ - Fix trusted RubyGems publishing for the terminal core.
8
+
9
+ ## 0.2.6 - 2026-09-21
10
+
11
+ - Add terminal damage tracking, synchronized-output state, focus notifications, and scrollback search.
12
+
5
13
  ## 0.2.5 - 2026-09-19
6
14
 
7
15
  - Keep open Windows ConPTY reads non-EOF while waiting for output.
data/lib/tarazed/grid.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
3
4
  require "unicode/display_width"
4
5
 
5
6
  module Tarazed
6
7
  class Grid
7
- attr_reader :columns, :rows, :cells, :scrollback, :cursor_x, :cursor_y, :scroll_top, :scroll_bottom
8
+ attr_reader :columns, :rows, :cells, :scrollback, :cursor_x, :cursor_y, :scroll_top, :scroll_bottom,
9
+ :synchronized
8
10
  attr_accessor :foreground, :background, :attributes, :hyperlink, :autowrap, :insert_mode, :origin_mode,
9
11
  :cursor_visible
10
12
 
@@ -25,10 +27,30 @@ module Tarazed
25
27
  @alternate = nil
26
28
  @scroll_top, @scroll_bottom = 0, rows - 1
27
29
  @cells = Array.new(rows) { blank_row }
30
+ @damage_rows = Set.new
31
+ @synchronized = false
32
+ mark_all_damage
28
33
  @tabs = (8...columns).step(8).to_a
29
34
  save_cursor
30
35
  end
31
36
 
37
+ # Return the inclusive range of rows changed since the last clear.
38
+ # A renderer can rebuild only this range and then call #clear_damage.
39
+ def damage
40
+ return if @damage_rows.empty?
41
+
42
+ @damage_rows.min..@damage_rows.max
43
+ end
44
+
45
+ def damage_rows = @damage_rows.to_a.sort.freeze
46
+ def clear_damage = (@damage_rows.clear; self)
47
+ def mark_damage(row) = (@damage_rows.add(row) if row.between?(0, rows - 1); self)
48
+ def mark_all_damage = (@damage_rows.merge(0...rows); self)
49
+ def synchronized? = @synchronized
50
+ def synchronized=(value)
51
+ @synchronized = !!value
52
+ end
53
+
32
54
  def [](row, column = nil) = column ? cells[row]&.[](column) : cells[row]
33
55
  def alternate? = !@alternate.nil?
34
56
  def lines = cells.map { |row| row.map(&:text).join.rstrip }
@@ -37,6 +59,7 @@ module Tarazed
37
59
  def wrap_pending? = @wrap_pending
38
60
 
39
61
  def put(char)
62
+ mark_damage(cursor_y)
40
63
  width = self.class.width(char)
41
64
  previous_x = @wrap_pending ? cursor_x : cursor_x - 1
42
65
  if previous_x >= 0 && !char.ascii_only?
@@ -113,6 +136,7 @@ module Tarazed
113
136
  @cursor_x = remainder.zero? ? columns - 1 : remainder
114
137
  @cursor_y = rows - 1
115
138
  @wrap_pending = remainder.zero?
139
+ mark_all_damage
116
140
  else
117
141
  text.each_byte { |byte| put(byte.chr) }
118
142
  end
@@ -125,6 +149,7 @@ module Tarazed
125
149
  end
126
150
 
127
151
  def move(x: cursor_x, y: cursor_y, relative: false)
152
+ previous_y = cursor_y
128
153
  if relative
129
154
  x += cursor_x
130
155
  y += cursor_y
@@ -133,6 +158,7 @@ module Tarazed
133
158
  @cursor_x = x.clamp(0, columns - 1)
134
159
  @cursor_y = y.clamp(top, bottom)
135
160
  @wrap_pending = false
161
+ mark_damage(previous_y).mark_damage(@cursor_y)
136
162
  end
137
163
 
138
164
  def position(row, column)
@@ -183,6 +209,7 @@ module Tarazed
183
209
  scrollback.push(removed) if scroll_top.zero? && scroll_bottom == rows - 1 && !alternate?
184
210
  cells.insert(scroll_bottom, blank_row)
185
211
  end
212
+ mark_all_damage
186
213
  end
187
214
 
188
215
  def scroll_down(count = 1)
@@ -190,6 +217,7 @@ module Tarazed
190
217
  cells.delete_at(scroll_bottom)
191
218
  cells.insert(scroll_top, blank_row)
192
219
  end
220
+ mark_all_damage
193
221
  end
194
222
 
195
223
  def erase_display(mode = 0)
@@ -203,6 +231,7 @@ module Tarazed
203
231
  when 2 then @cells = Array.new(rows) { blank_row }
204
232
  when 3 then scrollback.clear
205
233
  end
234
+ mark_all_damage
206
235
  end
207
236
 
208
237
  def erase_line(mode = 0)
@@ -216,6 +245,7 @@ module Tarazed
216
245
  clear_wide(cursor_y, column)
217
246
  cells[cursor_y][column] = blank
218
247
  end
248
+ mark_damage(cursor_y)
219
249
  end
220
250
 
221
251
  def erase_characters(count = 1)
@@ -223,6 +253,7 @@ module Tarazed
223
253
  clear_wide(cursor_y, column)
224
254
  cells[cursor_y][column] = blank
225
255
  end
256
+ mark_damage(cursor_y)
226
257
  end
227
258
 
228
259
  def insert_characters(count = 1)
@@ -230,6 +261,7 @@ module Tarazed
230
261
  cells[cursor_y].insert(cursor_x, *Array.new([count, columns - cursor_x].min) { blank })
231
262
  cells[cursor_y] = cells[cursor_y].first(columns)
232
263
  normalize_row(cursor_y)
264
+ mark_damage(cursor_y)
233
265
  end
234
266
 
235
267
  def delete_characters(count = 1)
@@ -238,6 +270,7 @@ module Tarazed
238
270
  cells[cursor_y].slice!(cursor_x, count)
239
271
  cells[cursor_y].concat(Array.new(count) { blank })
240
272
  normalize_row(cursor_y)
273
+ mark_damage(cursor_y)
241
274
  end
242
275
 
243
276
  def insert_lines(count = 1)
@@ -247,6 +280,7 @@ module Tarazed
247
280
  cells.delete_at(scroll_bottom)
248
281
  cells.insert(cursor_y, blank_row)
249
282
  end
283
+ mark_all_damage
250
284
  end
251
285
 
252
286
  def delete_lines(count = 1)
@@ -256,6 +290,7 @@ module Tarazed
256
290
  cells.delete_at(cursor_y)
257
291
  cells.insert(scroll_bottom, blank_row)
258
292
  end
293
+ mark_all_damage
259
294
  end
260
295
 
261
296
  def save_cursor
@@ -278,10 +313,12 @@ module Tarazed
278
313
  @cursor_x = @cursor_y = 0
279
314
  @scroll_top, @scroll_bottom = 0, rows - 1
280
315
  @wrap_pending = false
316
+ mark_all_damage
281
317
  elsif !enable && alternate?
282
318
  @cells, @alternate = @alternate, nil
283
319
  @scroll_top, @scroll_bottom = 0, rows - 1
284
320
  restore_cursor if save
321
+ mark_all_damage
285
322
  end
286
323
  end
287
324
 
@@ -307,6 +344,7 @@ module Tarazed
307
344
  @tabs = (@tabs.select { |column| column < columns } + added_tabs).uniq.sort
308
345
  @scroll_top, @scroll_bottom = 0, rows - 1
309
346
  move
347
+ mark_all_damage
310
348
  end
311
349
 
312
350
  # Coordinates are [column, row]; finish is exclusive, including scrollback
@@ -347,6 +385,11 @@ module Tarazed
347
385
  explicit.uniq
348
386
  end
349
387
 
388
+ def search(query, regex: false, history: true, normalize: true)
389
+ source = history ? scrollback.to_a + cells : cells
390
+ scrollback.search(query, regex: regex, normalize: normalize, rows: source)
391
+ end
392
+
350
393
  def file_paths(row, cwd: Dir.pwd)
351
394
  line = cells.fetch(row).map(&:text).join
352
395
  line.scan(%r{(?:\A|[\s("'])([^\s:"'()]+):(\d+)(?::(\d+))?}).filter_map do |name, number, column|
@@ -30,6 +30,25 @@ module Tarazed
30
30
  @rows << row.freeze
31
31
  end
32
32
 
33
+ def search(query, regex: false, normalize: true, rows: @rows)
34
+ matcher = regex ? Regexp.new(query.to_s) : query.to_s
35
+ rows.each_with_index.filter_map do |cells, index|
36
+ text = cells.map(&:text).join.rstrip
37
+ comparable = normalize && text.respond_to?(:unicode_normalize) ? text.unicode_normalize(:nfc) : text
38
+ match = regex ? matcher.match(comparable) : comparable.index(matcher)
39
+ next unless match
40
+
41
+ range = if match.is_a?(MatchData)
42
+ match.begin(0)...match.end(0)
43
+ else
44
+ match...(match + matcher.length)
45
+ end
46
+ {index: index, text: text, range: range}.freeze
47
+ end.freeze
48
+ rescue RegexpError => error
49
+ raise ArgumentError, "invalid search pattern: #{error.message}"
50
+ end
51
+
33
52
  def advance(count) = @total += count
34
53
  end
35
54
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tarazed
4
- VERSION = "0.2.5"
4
+ VERSION = "0.2.7"
5
5
  end
data/lib/tarazed/vt.rb CHANGED
@@ -92,6 +92,14 @@ module Tarazed
92
92
  modes[2004] ? "\e[200~#{text.gsub("\e[201~", "")}\e[201~" : text
93
93
  end
94
94
 
95
+ def synchronized? = !!modes[2026]
96
+
97
+ def focus(active:)
98
+ return "" unless modes[1004]
99
+
100
+ active ? "\e[I" : "\e[O"
101
+ end
102
+
95
103
  def mouse(button:, column:, row:, action: :press, shift: false, alt: false, control: false)
96
104
  tracking = [1000, 1002, 1003].find { |mode| modes[mode] }
97
105
  return "" unless tracking
@@ -355,6 +363,10 @@ module Tarazed
355
363
  when 1049 then grid.alternate(enabled)
356
364
  when 1000, 1002, 1003
357
365
  [1000, 1002, 1003].each { |other| @modes[other] = false unless other == mode } if enabled
366
+ when 1004
367
+ # Focus reporting is emitted by #focus when the host window changes.
368
+ when 2026
369
+ grid.synchronized = enabled
358
370
  end
359
371
  elsif mode == 4
360
372
  grid.insert_mode = enabled
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tarazed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada