tarazed 0.2.1 → 0.2.3
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/CHANGELOG.md +8 -0
- data/lib/tarazed/pty.rb +10 -1
- data/lib/tarazed/shell_integration.rb +1 -1
- data/lib/tarazed/version.rb +1 -1
- data/lib/tarazed/windows/conpty.rb +58 -22
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c41400c6ac1b8380eac0eaebe756d80425c06b4273f709e8902467276d622d80
|
|
4
|
+
data.tar.gz: 1a859372283bf896d16bacfd9b444ed02ad891279cdd0315cc8d072d8d7852f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3f007338660554ed33a141db5cf1b2fe4143837828cb5dbe723e083c4f9eba82d45593eef7c8c6ba3083a67150da1353515b34a9ff055a195e50cce788b83b5a
|
|
7
|
+
data.tar.gz: 912b5f4857e5cb99296ecfb7666eaeeb3c853397af6b8df1a94233f79c4aac3c0c9a3f5deb035d0f86d15ff260daaaa47ffddfb530da71f1eb36eb136f096191
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.3 - 2026-09-18
|
|
6
|
+
|
|
7
|
+
- Report the executable basename from string commands that include arguments.
|
|
8
|
+
|
|
9
|
+
## 0.2.2 - 2026-09-17
|
|
10
|
+
|
|
11
|
+
- Drain final Windows ConPTY output to EOF after the primary process exits without racing resize or close.
|
|
12
|
+
|
|
5
13
|
## 0.2.1 - 2026-09-16
|
|
6
14
|
|
|
7
15
|
- Report and cache natural Windows ConPTY process exit status through the cross-platform `PTY#status` API.
|
data/lib/tarazed/pty.rb
CHANGED
|
@@ -14,7 +14,16 @@ module Tarazed
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
@initial_cwd = File.expand_path(cwd)
|
|
17
|
-
|
|
17
|
+
executable = if command.is_a?(Array)
|
|
18
|
+
command.first
|
|
19
|
+
elsif Gem.win_platform?
|
|
20
|
+
value = command.to_s.lstrip
|
|
21
|
+
value.start_with?('"') ? value[/\A"([^"]*)"/, 1] : value.split(/\s/, 2).first
|
|
22
|
+
else
|
|
23
|
+
Shellwords.split(command.to_s).first
|
|
24
|
+
end
|
|
25
|
+
executable = executable.to_s.tr("\\", "/") if Gem.win_platform?
|
|
26
|
+
@command_name = File.basename(executable.to_s)
|
|
18
27
|
@grid = Grid.new(columns: columns, rows: rows, scrollback: scrollback)
|
|
19
28
|
if Gem.win_platform?
|
|
20
29
|
require_relative "windows/conpty"
|
|
@@ -8,7 +8,7 @@ module Tarazed
|
|
|
8
8
|
module_function
|
|
9
9
|
|
|
10
10
|
def path(shell)
|
|
11
|
-
name = File.basename(shell.to_s).to_sym
|
|
11
|
+
name = File.basename(shell.to_s).downcase.delete_suffix(".exe").to_sym
|
|
12
12
|
file = NAMES.fetch(name) { raise ArgumentError, "unsupported shell: #{shell}" }
|
|
13
13
|
File.expand_path("../../assets/shell-integration/#{file}", __dir__)
|
|
14
14
|
end
|
data/lib/tarazed/version.rb
CHANGED
|
@@ -22,7 +22,10 @@ module Tarazed
|
|
|
22
22
|
P, I, U, N, V = Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_UINT, Fiddle::TYPE_SIZE_T,
|
|
23
23
|
Fiddle::TYPE_VOID
|
|
24
24
|
STILL_ACTIVE = 259
|
|
25
|
-
|
|
25
|
+
WAIT_OBJECT_0 = 0
|
|
26
|
+
WAIT_TIMEOUT = 258
|
|
27
|
+
EXIT_POLL_MILLISECONDS = 10
|
|
28
|
+
private_constant :P, :I, :U, :N, :V, :WAIT_OBJECT_0, :WAIT_TIMEOUT, :EXIT_POLL_MILLISECONDS
|
|
26
29
|
|
|
27
30
|
class Status
|
|
28
31
|
attr_reader :exitstatus
|
|
@@ -41,6 +44,7 @@ module Tarazed
|
|
|
41
44
|
attr_reader :pid
|
|
42
45
|
|
|
43
46
|
def initialize(command: nil, columns: 80, rows: 24, cwd: nil, env: {}, kernel: nil)
|
|
47
|
+
@console_lock, @lifecycle_lock = Mutex.new, Mutex.new
|
|
44
48
|
raise Error, "ConPTY requires 64-bit Windows Ruby" unless Fiddle::SIZEOF_VOIDP == 8
|
|
45
49
|
|
|
46
50
|
@kernel = kernel || Library.new("kernel32.dll")
|
|
@@ -55,6 +59,7 @@ module Tarazed
|
|
|
55
59
|
attributes = process_attributes(@console)
|
|
56
60
|
@process, @pid = spawn(command, cwd, env, attributes)
|
|
57
61
|
start_reader
|
|
62
|
+
start_exit_watcher
|
|
58
63
|
rescue Fiddle::DLError, LoadError => error
|
|
59
64
|
release
|
|
60
65
|
raise Error, "ConPTY is unavailable: #{error.message}"
|
|
@@ -116,7 +121,12 @@ module Tarazed
|
|
|
116
121
|
end
|
|
117
122
|
|
|
118
123
|
def resize(columns, rows)
|
|
119
|
-
|
|
124
|
+
size = coordinate(columns, rows)
|
|
125
|
+
@console_lock.synchronize do
|
|
126
|
+
return unless @console
|
|
127
|
+
|
|
128
|
+
check_hresult(function(:ResizePseudoConsole, [P, I], I).call(@console, size))
|
|
129
|
+
end
|
|
120
130
|
end
|
|
121
131
|
|
|
122
132
|
def status
|
|
@@ -127,17 +137,20 @@ module Tarazed
|
|
|
127
137
|
def alive? = !@closed && !status
|
|
128
138
|
|
|
129
139
|
def close
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
@lifecycle_lock.synchronize do
|
|
141
|
+
return self if @closed
|
|
142
|
+
|
|
143
|
+
safely { refresh_status }
|
|
144
|
+
@closed = true
|
|
145
|
+
close_handle(@input_write)
|
|
146
|
+
@input_write = nil
|
|
147
|
+
close_console
|
|
148
|
+
@watcher&.join
|
|
149
|
+
@reader&.join(2)
|
|
150
|
+
close_handle(@output_read)
|
|
151
|
+
close_handle(@process)
|
|
152
|
+
@output_read = @process = nil
|
|
153
|
+
end
|
|
141
154
|
self
|
|
142
155
|
end
|
|
143
156
|
|
|
@@ -241,20 +254,43 @@ module Tarazed
|
|
|
241
254
|
end
|
|
242
255
|
end
|
|
243
256
|
|
|
257
|
+
def start_exit_watcher
|
|
258
|
+
wait = function(:WaitForSingleObject, [P, U], U, need_gvl: false)
|
|
259
|
+
@watcher = Thread.new do
|
|
260
|
+
loop do
|
|
261
|
+
result = wait.call(@process, EXIT_POLL_MILLISECONDS)
|
|
262
|
+
break if @closed
|
|
263
|
+
if result == WAIT_OBJECT_0
|
|
264
|
+
close_console
|
|
265
|
+
break
|
|
266
|
+
end
|
|
267
|
+
raise SystemCallError.new("WaitForSingleObject", Fiddle.last_error) unless result == WAIT_TIMEOUT
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
244
272
|
def close_console
|
|
245
|
-
|
|
273
|
+
console = @console_lock.synchronize do
|
|
274
|
+
value = @console
|
|
275
|
+
@console = nil
|
|
276
|
+
value
|
|
277
|
+
end
|
|
278
|
+
return unless console
|
|
246
279
|
|
|
247
|
-
function(:ClosePseudoConsole, [P], V, need_gvl: false).call(
|
|
248
|
-
@console = nil
|
|
280
|
+
function(:ClosePseudoConsole, [P], V, need_gvl: false).call(console)
|
|
249
281
|
end
|
|
250
282
|
|
|
251
283
|
def release
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
284
|
+
@lifecycle_lock.synchronize do
|
|
285
|
+
@closed = true
|
|
286
|
+
safely { close_handle(@input_write) }
|
|
287
|
+
safely { close_console }
|
|
288
|
+
@watcher&.join
|
|
289
|
+
@reader&.join(2)
|
|
290
|
+
safely { close_handle(@output_read) }
|
|
291
|
+
safely { close_handle(@process) }
|
|
292
|
+
@input_write = @output_read = @process = nil
|
|
293
|
+
end
|
|
258
294
|
end
|
|
259
295
|
|
|
260
296
|
def safely
|