tty-pager 0.9.0 → 0.13.0
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 +5 -5
- data/CHANGELOG.md +81 -10
- data/README.md +247 -29
- data/lib/tty-pager.rb +1 -3
- data/lib/tty/pager.rb +82 -83
- data/lib/tty/pager/abstract.rb +138 -0
- data/lib/tty/pager/basic.rb +198 -56
- data/lib/tty/pager/null.rb +22 -4
- data/lib/tty/pager/system.rb +185 -73
- data/lib/tty/pager/version.rb +3 -3
- metadata +31 -89
- data/.gitignore +0 -22
- data/.rspec +0 -3
- data/.travis.yml +0 -25
- data/CODE_OF_CONDUCT.md +0 -49
- data/Gemfile +0 -14
- data/Rakefile +0 -8
- data/appveyor.yml +0 -25
- data/examples/basic_pager.rb +0 -7
- data/examples/system_pager.rb +0 -9
- data/examples/temp.txt +0 -49
- data/spec/spec_helper.rb +0 -45
- data/spec/unit/basic/page_spec.rb +0 -142
- data/spec/unit/null/page_spec.rb +0 -23
- data/spec/unit/page_spec.rb +0 -40
- data/spec/unit/system/available_spec.rb +0 -50
- data/spec/unit/system/command_exists_spec.rb +0 -15
- data/spec/unit/system/new_spec.rb +0 -10
- data/spec/unit/system/page_spec.rb +0 -21
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-pager.gemspec +0 -28
data/lib/tty/pager/null.rb
CHANGED
@@ -1,17 +1,35 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
3
|
+
require_relative "abstract"
|
4
|
+
|
4
5
|
module TTY
|
5
|
-
|
6
|
-
class NullPager <
|
6
|
+
module Pager
|
7
|
+
class NullPager < Abstract
|
8
|
+
# Pass output directly to stdout
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
def write(text)
|
12
|
+
return text unless output.tty?
|
13
|
+
|
14
|
+
output.write(text)
|
15
|
+
end
|
16
|
+
alias << write
|
17
|
+
|
7
18
|
# Pass output directly to stdout
|
8
19
|
#
|
9
20
|
# @api public
|
10
|
-
def
|
21
|
+
def puts(text)
|
11
22
|
return text unless output.tty?
|
12
23
|
|
13
24
|
output.puts(text)
|
14
25
|
end
|
26
|
+
|
27
|
+
# Do nothing, always return success
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def close
|
31
|
+
true
|
32
|
+
end
|
15
33
|
end
|
16
34
|
end # Pager
|
17
35
|
end # TTY
|
data/lib/tty/pager/system.rb
CHANGED
@@ -1,51 +1,92 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
require
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
require_relative "abstract"
|
5
6
|
|
6
7
|
module TTY
|
7
|
-
|
8
|
+
module Pager
|
8
9
|
# A system pager is used on systems where native
|
9
10
|
# pagination exists
|
10
11
|
#
|
11
12
|
# @api public
|
12
|
-
class SystemPager <
|
13
|
-
#
|
13
|
+
class SystemPager < Abstract
|
14
|
+
# Check if command exists
|
14
15
|
#
|
15
|
-
# @
|
16
|
-
#
|
17
|
-
# the command to use for paging
|
16
|
+
# @example
|
17
|
+
# command_exist?("less") # => true
|
18
18
|
#
|
19
|
-
# @
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
# @param [String] command
|
20
|
+
# the command to check
|
21
|
+
#
|
22
|
+
# @return [Boolean]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
def self.command_exist?(command)
|
26
|
+
exts = ENV.fetch("PATHEXT", "").split(::File::PATH_SEPARATOR)
|
27
|
+
ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).any? do |dir|
|
28
|
+
file = ::File.join(dir, command)
|
29
|
+
::File.exist?(file) || exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
#
|
33
|
+
# Run pager command silently with no input and capture output
|
34
|
+
#
|
35
|
+
# @return [Boolean]
|
36
|
+
# true if command runs successfully, false otherwise
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
def self.run_command(*args)
|
40
|
+
_, err, status = Open3.capture3(*args)
|
41
|
+
err.empty? && status.success?
|
42
|
+
rescue Errno::ENOENT
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
# List possible executables for output paging
|
47
|
+
#
|
48
|
+
# The UNIX systems often come with "pg" and "more" but no "less" utility.
|
49
|
+
# The Linux usually provides "less" and "more" pager, but often no "pg".
|
50
|
+
# MacOS comes with "less" and "more" pager and no "pg".
|
51
|
+
# Windows provides "more".
|
52
|
+
# The "more" pager is the oldest utility and thus most compatible
|
53
|
+
# with many systems.
|
54
|
+
#
|
55
|
+
# @return [Array[String]]
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
def self.executables
|
59
|
+
[ENV["GIT_PAGER"], ENV["PAGER"], git_pager,
|
60
|
+
"less -r", "more -r", "most", "pg", "cat", "pager"].compact
|
61
|
+
end
|
62
|
+
|
63
|
+
# Finds git pager configuration
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
def self.git_pager
|
67
|
+
command_exist?("git") ? `git config --get-all core.pager` : nil
|
68
|
+
end
|
69
|
+
private_class_method :git_pager
|
70
|
+
|
71
|
+
# Find first available termainal pager program executable
|
32
72
|
#
|
33
73
|
# @example Basic usage
|
34
|
-
#
|
74
|
+
# find_executable # => "less"
|
35
75
|
#
|
36
76
|
# @example Usage with commands
|
37
|
-
#
|
77
|
+
# find_executable("less", "cat") # => "less"
|
38
78
|
#
|
39
79
|
# @param [Array[String]] commands
|
40
80
|
#
|
41
|
-
# @return [String]
|
81
|
+
# @return [String, nil]
|
82
|
+
# the found executable or nil when not found
|
42
83
|
#
|
43
84
|
# @api public
|
44
|
-
def self.
|
45
|
-
|
46
|
-
|
85
|
+
def self.find_executable(*commands)
|
86
|
+
execs = commands.empty? ? executables : commands
|
87
|
+
execs
|
47
88
|
.compact.map(&:strip).reject(&:empty?).uniq
|
48
|
-
.find { |cmd|
|
89
|
+
.find { |cmd| command_exist?(cmd.split.first) }
|
49
90
|
end
|
50
91
|
|
51
92
|
# Check if command is available
|
@@ -54,72 +95,84 @@ module TTY
|
|
54
95
|
# available? # => true
|
55
96
|
#
|
56
97
|
# @example Usage with command
|
57
|
-
# available?(
|
98
|
+
# available?("less") # => true
|
58
99
|
#
|
59
100
|
# @return [Boolean]
|
60
101
|
#
|
61
102
|
# @api public
|
62
|
-
def self.
|
63
|
-
!
|
103
|
+
def self.exec_available?(*commands)
|
104
|
+
!find_executable(*commands).nil?
|
64
105
|
end
|
65
106
|
|
66
|
-
#
|
67
|
-
#
|
68
|
-
# @example
|
69
|
-
# page('some long text...')
|
70
|
-
#
|
71
|
-
# @param [String] text
|
72
|
-
# the text to paginate
|
107
|
+
# Create a system pager
|
73
108
|
#
|
74
|
-
# @
|
75
|
-
# the
|
109
|
+
# @param [String] :command
|
110
|
+
# the command to use for paging
|
76
111
|
#
|
77
112
|
# @api public
|
78
|
-
def
|
79
|
-
|
113
|
+
def initialize(command: nil, **options)
|
114
|
+
super(**options)
|
115
|
+
@pager_io = nil
|
116
|
+
@pager_command = nil
|
117
|
+
pager_command(*Array(command))
|
80
118
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
_, status = Process.waitpid2(pid, Process::WNOHANG)
|
88
|
-
status.success?
|
89
|
-
rescue Errno::ECHILD
|
90
|
-
# on jruby 9x waiting on pid raises
|
91
|
-
true
|
119
|
+
if pager_command.nil?
|
120
|
+
raise TTY::Pager::Error,
|
121
|
+
"#{self.class.name} cannot be used on your system due to " \
|
122
|
+
"lack of appropriate pager executable. Install `less` like " \
|
123
|
+
"pager or try using `BasicPager` instead."
|
124
|
+
end
|
92
125
|
end
|
93
126
|
|
94
|
-
|
95
|
-
|
96
|
-
# List possible executables for output paging
|
127
|
+
# Send text to the pager process. Starts a new process if it hasn't been
|
128
|
+
# started yet.
|
97
129
|
#
|
98
|
-
# @
|
130
|
+
# @param [Array<String>] *args
|
131
|
+
# strings to send to the pager
|
99
132
|
#
|
100
|
-
# @
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
133
|
+
# @raise [PagerClosed]
|
134
|
+
# strings to send to the pager
|
135
|
+
#
|
136
|
+
# @api public
|
137
|
+
def write(*args)
|
138
|
+
@pager_io ||= spawn_pager
|
139
|
+
@pager_io.write(*args)
|
140
|
+
self
|
105
141
|
end
|
106
|
-
|
142
|
+
alias << write
|
107
143
|
|
108
|
-
#
|
144
|
+
# Send a line of text, ending in a newline, to the pager process. Starts
|
145
|
+
# a new process if it hasn't been started yet.
|
109
146
|
#
|
110
|
-
# @
|
111
|
-
#
|
147
|
+
# @raise [PagerClosed]
|
148
|
+
# if the pager was closed
|
112
149
|
#
|
113
|
-
# @
|
114
|
-
#
|
150
|
+
# @return [SystemPager]
|
151
|
+
#
|
152
|
+
# @api public
|
153
|
+
def puts(text)
|
154
|
+
@pager_io ||= spawn_pager
|
155
|
+
@pager_io.puts(text)
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
# Stop the pager, wait for the process to finish. If no pager has been
|
160
|
+
# started, returns true.
|
115
161
|
#
|
116
162
|
# @return [Boolean]
|
163
|
+
# the exit status of the child process
|
117
164
|
#
|
118
|
-
# @api
|
119
|
-
def
|
120
|
-
|
165
|
+
# @api public
|
166
|
+
def close
|
167
|
+
return true unless @pager_io
|
168
|
+
|
169
|
+
success = @pager_io.close
|
170
|
+
@pager_io = nil
|
171
|
+
success
|
121
172
|
end
|
122
173
|
|
174
|
+
private
|
175
|
+
|
123
176
|
# The pager command to run
|
124
177
|
#
|
125
178
|
# @return [String]
|
@@ -127,11 +180,70 @@ module TTY
|
|
127
180
|
#
|
128
181
|
# @api private
|
129
182
|
def pager_command(*commands)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
183
|
+
if @pager_command && commands.empty?
|
184
|
+
@pager_command
|
185
|
+
else
|
186
|
+
@pager_command = self.class.find_executable(*commands)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Spawn the pager process
|
191
|
+
#
|
192
|
+
# @return [PagerIO]
|
193
|
+
# A wrapper for the external pager
|
194
|
+
#
|
195
|
+
# @api private
|
196
|
+
def spawn_pager
|
197
|
+
# In case there's a previous pager running:
|
198
|
+
close
|
199
|
+
|
200
|
+
command = pager_command
|
201
|
+
status = self.class.run_command(command)
|
202
|
+
# Issue running command, e.g. unsupported flag, fallback to just command
|
203
|
+
unless status
|
204
|
+
command = pager_command.split.first
|
205
|
+
end
|
206
|
+
|
207
|
+
PagerIO.new(command)
|
208
|
+
end
|
209
|
+
|
210
|
+
# A wrapper for an external process.
|
211
|
+
#
|
212
|
+
# @api private
|
213
|
+
class PagerIO
|
214
|
+
def initialize(command)
|
215
|
+
@command = command
|
216
|
+
@io = IO.popen(@command, "w")
|
217
|
+
@pid = @io.pid
|
218
|
+
end
|
219
|
+
|
220
|
+
def write(*args)
|
221
|
+
io_call(:write, *args)
|
222
|
+
end
|
223
|
+
|
224
|
+
def puts(*args)
|
225
|
+
io_call(:puts, *args)
|
226
|
+
end
|
227
|
+
|
228
|
+
def close
|
229
|
+
return true if @io.closed?
|
230
|
+
|
231
|
+
@io.close
|
232
|
+
_, status = Process.waitpid2(@pid, Process::WNOHANG)
|
233
|
+
status.success?
|
234
|
+
rescue Errno::ECHILD, Errno::EPIPE
|
235
|
+
# on jruby 9x waiting on pid raises ECHILD
|
236
|
+
# on ruby 2.5/2.6, closing a closed pipe raises EPIPE
|
237
|
+
true
|
238
|
+
end
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
def io_call(method_name, *args)
|
243
|
+
@io.public_send(method_name, *args)
|
244
|
+
rescue Errno::EPIPE
|
245
|
+
raise PagerClosed.new("The pager process (`#{@command}`) was closed")
|
246
|
+
end
|
135
247
|
end
|
136
248
|
end # SystemPager
|
137
249
|
end # Pager
|
data/lib/tty/pager/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tty-pager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tty-screen
|
@@ -16,62 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: '0.8'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: '0.8'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: strings
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.1.8
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: verse
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 0.5.0
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 0.5.0
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: bundler
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 1.5.0
|
62
|
-
- - "<"
|
63
|
-
- !ruby/object:Gem::Version
|
64
|
-
version: '2.0'
|
65
|
-
type: :development
|
66
|
-
prerelease: false
|
67
|
-
version_requirements: !ruby/object:Gem::Requirement
|
68
|
-
requirements:
|
69
|
-
- - ">="
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
version: 1.5.0
|
72
|
-
- - "<"
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
version: '2.0'
|
40
|
+
version: 0.1.8
|
75
41
|
- !ruby/object:Gem::Dependency
|
76
42
|
name: rake
|
77
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -90,59 +56,46 @@ dependencies:
|
|
90
56
|
name: rspec
|
91
57
|
requirement: !ruby/object:Gem::Requirement
|
92
58
|
requirements:
|
93
|
-
- - "
|
59
|
+
- - ">="
|
94
60
|
- !ruby/object:Gem::Version
|
95
|
-
version: 3.
|
61
|
+
version: '3.0'
|
96
62
|
type: :development
|
97
63
|
prerelease: false
|
98
64
|
version_requirements: !ruby/object:Gem::Requirement
|
99
65
|
requirements:
|
100
|
-
- - "
|
66
|
+
- - ">="
|
101
67
|
- !ruby/object:Gem::Version
|
102
|
-
version: 3.
|
103
|
-
description:
|
104
|
-
interpreters.
|
68
|
+
version: '3.0'
|
69
|
+
description: A cross-platform terminal pager that works on all major Ruby interpreters.
|
105
70
|
email:
|
106
|
-
-
|
71
|
+
- piotr@piotrmurach.com
|
107
72
|
executables: []
|
108
73
|
extensions: []
|
109
|
-
extra_rdoc_files:
|
74
|
+
extra_rdoc_files:
|
75
|
+
- README.md
|
76
|
+
- CHANGELOG.md
|
77
|
+
- LICENSE.txt
|
110
78
|
files:
|
111
|
-
- ".gitignore"
|
112
|
-
- ".rspec"
|
113
|
-
- ".travis.yml"
|
114
79
|
- CHANGELOG.md
|
115
|
-
- CODE_OF_CONDUCT.md
|
116
|
-
- Gemfile
|
117
80
|
- LICENSE.txt
|
118
81
|
- README.md
|
119
|
-
- Rakefile
|
120
|
-
- appveyor.yml
|
121
|
-
- examples/basic_pager.rb
|
122
|
-
- examples/system_pager.rb
|
123
|
-
- examples/temp.txt
|
124
82
|
- lib/tty-pager.rb
|
125
83
|
- lib/tty/pager.rb
|
84
|
+
- lib/tty/pager/abstract.rb
|
126
85
|
- lib/tty/pager/basic.rb
|
127
86
|
- lib/tty/pager/null.rb
|
128
87
|
- lib/tty/pager/system.rb
|
129
88
|
- lib/tty/pager/version.rb
|
130
|
-
|
131
|
-
- spec/unit/basic/page_spec.rb
|
132
|
-
- spec/unit/null/page_spec.rb
|
133
|
-
- spec/unit/page_spec.rb
|
134
|
-
- spec/unit/system/available_spec.rb
|
135
|
-
- spec/unit/system/command_exists_spec.rb
|
136
|
-
- spec/unit/system/new_spec.rb
|
137
|
-
- spec/unit/system/page_spec.rb
|
138
|
-
- tasks/console.rake
|
139
|
-
- tasks/coverage.rake
|
140
|
-
- tasks/spec.rake
|
141
|
-
- tty-pager.gemspec
|
142
|
-
homepage: https://github.com/piotrmurach/tty-pager
|
89
|
+
homepage: https://piotrmurach.github.io/tty
|
143
90
|
licenses:
|
144
91
|
- MIT
|
145
|
-
metadata:
|
92
|
+
metadata:
|
93
|
+
allowed_push_host: https://rubygems.org
|
94
|
+
bug_tracker_uri: https://github.com/piotrmurach/tty-pager/issues
|
95
|
+
changelog_uri: https://github.com/piotrmurach/tty-pager/blob/master/CHANGELOG.md
|
96
|
+
documentation_uri: https://www.rubydoc.info/gems/tty-pager
|
97
|
+
homepage_uri: https://piotrmurach.github.io/tty
|
98
|
+
source_code_uri: https://github.com/piotrmurach/tty-pager
|
146
99
|
post_install_message:
|
147
100
|
rdoc_options: []
|
148
101
|
require_paths:
|
@@ -151,26 +104,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
151
104
|
requirements:
|
152
105
|
- - ">="
|
153
106
|
- !ruby/object:Gem::Version
|
154
|
-
version:
|
107
|
+
version: 2.0.0
|
155
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
109
|
requirements:
|
157
110
|
- - ">="
|
158
111
|
- !ruby/object:Gem::Version
|
159
112
|
version: '0'
|
160
113
|
requirements: []
|
161
|
-
|
162
|
-
rubygems_version: 2.5.1
|
114
|
+
rubygems_version: 3.1.2
|
163
115
|
signing_key:
|
164
116
|
specification_version: 4
|
165
|
-
summary:
|
166
|
-
|
167
|
-
test_files:
|
168
|
-
- spec/spec_helper.rb
|
169
|
-
- spec/unit/basic/page_spec.rb
|
170
|
-
- spec/unit/null/page_spec.rb
|
171
|
-
- spec/unit/page_spec.rb
|
172
|
-
- spec/unit/system/available_spec.rb
|
173
|
-
- spec/unit/system/command_exists_spec.rb
|
174
|
-
- spec/unit/system/new_spec.rb
|
175
|
-
- spec/unit/system/page_spec.rb
|
176
|
-
has_rdoc:
|
117
|
+
summary: A cross-platform terminal pager that works on all major Ruby interpreters.
|
118
|
+
test_files: []
|