tty-link 0.1.1 → 0.2.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.
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract"
4
+
5
+ module TTY
6
+ class Link
7
+ module Terminals
8
+ # Responsible for detecting hyperlink support in the WezTerm terminal
9
+ #
10
+ # @api private
11
+ class Wezterm < Abstract
12
+ # The WezTerm terminal name pattern
13
+ #
14
+ # @return [Regexp]
15
+ #
16
+ # @api private
17
+ WEZTERM = /WezTerm/i.freeze
18
+ private_constant :WEZTERM
19
+
20
+ # The version release
21
+ #
22
+ # @return [String]
23
+ #
24
+ # @api private
25
+ VERSION_RELEASE = "20180218"
26
+ private_constant :VERSION_RELEASE
27
+
28
+ # The version separator
29
+ #
30
+ # @return [String]
31
+ #
32
+ # @api private
33
+ VERSION_SEPARATOR = "-"
34
+ private_constant :VERSION_SEPARATOR
35
+
36
+ private
37
+
38
+ # Detect WezTerm terminal
39
+ #
40
+ # @example
41
+ # wezterm.name?
42
+ # # => true
43
+ #
44
+ # @return [Boolean]
45
+ #
46
+ # @api private
47
+ def name?
48
+ !(term_program =~ WEZTERM).nil?
49
+ end
50
+
51
+ # Detect whether the WezTerm version supports terminal hyperlinks
52
+ #
53
+ # @example
54
+ # wezterm.version?
55
+ # # => true
56
+ #
57
+ # @return [Boolean]
58
+ #
59
+ # @api private
60
+ def version?
61
+ return false unless term_program_version
62
+
63
+ current_semantic_version =
64
+ semantic_version(term_program_version, separator: VERSION_SEPARATOR)
65
+
66
+ current_semantic_version >= semantic_version(VERSION_RELEASE, 0, 0)
67
+ end
68
+ end # Wezterm
69
+ end # Terminals
70
+ end # Link
71
+ end # TTY
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract"
4
+
5
+ module TTY
6
+ class Link
7
+ module Terminals
8
+ # Responsible for detecting hyperlink support in the Windows Terminal
9
+ #
10
+ # @api private
11
+ class Wt < Abstract
12
+ # The wt session environment variable name
13
+ #
14
+ # @return [String]
15
+ #
16
+ # @api private
17
+ WT_SESSION = "WT_SESSION"
18
+ private_constant :WT_SESSION
19
+
20
+ private
21
+
22
+ # Detect Windows Terminal
23
+ #
24
+ # @example
25
+ # wt.name?
26
+ # # => true
27
+ #
28
+ # @return [Boolean]
29
+ #
30
+ # @api private
31
+ def name?
32
+ !wt_session.nil?
33
+ end
34
+
35
+ # Detect any Windows Terminal version to support terminal hyperlinks
36
+ #
37
+ # @example
38
+ # wt.version?
39
+ # # => true
40
+ #
41
+ # @return [Boolean]
42
+ #
43
+ # @api private
44
+ def version?
45
+ true
46
+ end
47
+
48
+ # Read the wt session environment variable
49
+ #
50
+ # @example
51
+ # wt.wt_session
52
+ # # => "the-unique-identifier"
53
+ #
54
+ # @return [String, nil]
55
+ #
56
+ # @api private
57
+ def wt_session
58
+ env[WT_SESSION]
59
+ end
60
+ end # Wt
61
+ end # Terminals
62
+ end # Link
63
+ end # TTY
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTY
4
+ class Link
5
+ # Responsible for loading terminals
6
+ #
7
+ # @api private
8
+ module Terminals
9
+ # The directory name for terminals
10
+ #
11
+ # @return [String]
12
+ #
13
+ # @api private
14
+ DIR_NAME = "terminals"
15
+ private_constant :DIR_NAME
16
+
17
+ # The Ruby file pattern
18
+ #
19
+ # @return [String]
20
+ #
21
+ # @api private
22
+ RUBY_FILE = "*.rb"
23
+ private_constant :RUBY_FILE
24
+
25
+ # The registered terminal classes
26
+ #
27
+ # @example
28
+ # TTY::Link::Terminals.registered
29
+ #
30
+ # @return [Array<TTY::Link::Terminals::Abstract>]
31
+ #
32
+ # @api private
33
+ def self.registered
34
+ @registered ||= []
35
+ end
36
+
37
+ # Register a terminal class
38
+ #
39
+ # @example
40
+ # TTY::Link::Terminals.register(TTY::Link::Terminals::Iterm)
41
+ #
42
+ # @param [TTY::Link::Terminals::Abstract] terminal_class
43
+ # the terminal class to register
44
+ #
45
+ # @return [void]
46
+ #
47
+ # @api private
48
+ def self.register(terminal_class)
49
+ registered << terminal_class
50
+ end
51
+
52
+ # Require all terminal files from the terminals directory
53
+ #
54
+ # @example
55
+ # TTY::Link::Terminals.require_terminals
56
+ #
57
+ # @return [void]
58
+ #
59
+ # @api private
60
+ def self.require_terminals
61
+ terminals_dir = ::File.join(__dir__, DIR_NAME, RUBY_FILE)
62
+ ::Dir.glob(terminals_dir).sort.each do |terminal_path|
63
+ require_relative ::File.join(DIR_NAME, ::File.basename(terminal_path))
64
+ end
65
+ end
66
+ private_class_method :require_terminals
67
+
68
+ require_terminals
69
+ end
70
+ end # Link
71
+ end # TTY
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TTY
4
- module Link
5
- VERSION = "0.1.1"
4
+ class Link
5
+ VERSION = "0.2.0"
6
6
  end # Link
7
7
  end # TTY
data/lib/tty/link.rb CHANGED
@@ -1,74 +1,313 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "link/ansi_link"
4
+ require_relative "link/errors"
5
+ require_relative "link/hyperlink_parameter"
6
+ require_relative "link/plain_link"
7
+ require_relative "link/semantic_version"
8
+ require_relative "link/terminals"
3
9
  require_relative "link/version"
4
10
 
5
11
  module TTY
6
- module Link
7
- class Error < StandardError; end
12
+ # Responsible for detecting and generating terminal hyperlinks
13
+ #
14
+ # @api public
15
+ class Link
16
+ # The default plain URL template
17
+ #
18
+ # @return [String]
19
+ #
20
+ # @api private
21
+ DEFAULT_TEMPLATE = ":name -> :url"
22
+ private_constant :DEFAULT_TEMPLATE
8
23
 
9
- ESC = "\u001B["
10
- OSC = "\u001B]"
11
- BEL = "\u0007"
12
- SEP = ";"
24
+ # The hyperlink environment variable name
25
+ #
26
+ # @return [String]
27
+ #
28
+ # @api private
29
+ HYPERLINK_ENV = "TTY_LINK_HYPERLINK"
30
+ private_constant :HYPERLINK_ENV
13
31
 
14
- ITERM = /iTerm(\s*\d+){0,1}.app/x.freeze
32
+ # Generate terminal hyperlink
33
+ #
34
+ # @example
35
+ # TTY::Link.link_to("TTY Toolkit", "https://ttytoolkit.org")
36
+ #
37
+ # @example
38
+ # TTY::Link.link_to("https://ttytoolkit.org")
39
+ #
40
+ # @example
41
+ # TTY::Link.link_to("TTY Toolkit", "https://ttytoolkit.org",
42
+ # attrs: {id: "tty-toolkit"})
43
+ #
44
+ # @example
45
+ # TTY::Link.link_to("TTY Toolkit", "https://ttytoolkit.org",
46
+ # env: {"VTE_VERSION" => "7603"})
47
+ #
48
+ # @example
49
+ # TTY::Link.link_to("TTY Toolkit", "https://ttytoolkit.org",
50
+ # hyperlink: :always)
51
+ #
52
+ # @example
53
+ # TTY::Link.link_to("TTY Toolkit", "https://ttytoolkit.org",
54
+ # output: $stderr)
55
+ #
56
+ # @example
57
+ # TTY::Link.link_to("TTY Toolkit", "https://ttytoolkit.org",
58
+ # plain: ":name (:url)")
59
+ #
60
+ # @param [String] name
61
+ # the name for the URL
62
+ # @param [String, nil] url
63
+ # the URL target
64
+ # @param [Hash{Symbol => String}] attrs
65
+ # the URL attributes
66
+ # @param [ENV, Hash{String => String}] env
67
+ # the environment variables
68
+ # @param [String, Symbol] hyperlink
69
+ # the hyperlink detection out of always, auto or never
70
+ # @param [IO] output
71
+ # the output stream, defaults to $stdout
72
+ # @param [String] plain
73
+ # the plain URL template
74
+ #
75
+ # @return [String]
76
+ #
77
+ # @see #link_to
78
+ #
79
+ # @api public
80
+ def self.link_to(name, url = nil, attrs: {}, env: ENV, hyperlink: :auto,
81
+ output: $stdout, plain: DEFAULT_TEMPLATE)
82
+ new(env: env, hyperlink: hyperlink, output: output, plain: plain)
83
+ .link_to(name, url, attrs: attrs)
84
+ end
15
85
 
16
- # Parse version number
86
+ # Detect terminal hyperlink support
17
87
  #
18
- # @param [String] version
88
+ # @example
89
+ # TTY::Link.link?
90
+ # # => true
19
91
  #
20
- # @api private
21
- def parse_version(version)
22
- if (matches = version.match(/^(\d{1,2})(\d{2})$/))
23
- major, minor, patch = 0, matches[1].to_i, matches[2].to_i
92
+ # @example
93
+ # TTY::Link.link?(env: {"VTE_VERSION" => "7603"})
94
+ # # => true
95
+ #
96
+ # @example
97
+ # TTY::Link.link?(output: $stderr)
98
+ # # => false
99
+ #
100
+ # @param [ENV, Hash{String => String}] env
101
+ # the environment variables
102
+ # @param [IO] output
103
+ # the output stream, defaults to $stdout
104
+ #
105
+ # @return [Boolean]
106
+ #
107
+ # @see #link?
108
+ #
109
+ # @api public
110
+ def self.link?(env: ENV, output: $stdout)
111
+ new(env: env, output: output).link?
112
+ end
113
+
114
+ # Create a {TTY::Link} instance
115
+ #
116
+ # @example
117
+ # link = TTY::Link.new
118
+ #
119
+ # @example
120
+ # link = TTY::Link.new(env: {"VTE_VERSION" => "7603"})
121
+ #
122
+ # @example
123
+ # link = TTY::Link.new(hyperlink: :always)
124
+ #
125
+ # @example
126
+ # link = TTY::Link.new(output: $stderr)
127
+ #
128
+ # @example
129
+ # link = TTY::Link.new(plain: ":name (:url)")
130
+ #
131
+ # @param [ENV, Hash{String => String}] env
132
+ # the environment variables
133
+ # @param [String, Symbol] hyperlink
134
+ # the hyperlink detection out of always, auto or never
135
+ # @param [IO] output
136
+ # the output stream, defaults to $stdout
137
+ # @param [String] plain
138
+ # the plain URL template
139
+ #
140
+ # @api public
141
+ def initialize(env: ENV, hyperlink: :auto, output: $stdout,
142
+ plain: DEFAULT_TEMPLATE)
143
+ @env = env
144
+ @hyperlink_parameter = hyperlink_parameter(hyperlink_env || hyperlink)
145
+ @output = output
146
+ @plain = plain
147
+ end
148
+
149
+ # Generate terminal hyperlink
150
+ #
151
+ # @example
152
+ # link.link_to("TTY Toolkit", "https://ttytoolkit.org")
153
+ #
154
+ # @example
155
+ # link.link_to("https://ttytoolkit.org")
156
+ #
157
+ # @example
158
+ # link.link_to("TTY Toolkit", "https://ttytoolkit.org",
159
+ # attrs: {id: "tty-toolkit"})
160
+ #
161
+ # @example
162
+ # link.link_to("https://ttytoolkit.org",
163
+ # attrs: {id: "tty-toolkit", title: "TTY Toolkit"})
164
+ #
165
+ # @param [String] name
166
+ # the name for the URL
167
+ # @param [String, nil] url
168
+ # the URL target
169
+ # @param [Hash{Symbol => String}] attrs
170
+ # the URL attributes
171
+ #
172
+ # @return [String]
173
+ #
174
+ # @api public
175
+ def link_to(name, url = nil, attrs: {})
176
+ url ||= name
177
+
178
+ if ansi_link?
179
+ ansi_link(name, url, attrs).to_s
24
180
  else
25
- major, minor, patch = version.split(".").map(&:to_i)
181
+ plain_link(name, url).to_s
26
182
  end
27
- { major: major, minor: minor, patch: patch }
28
183
  end
29
- module_function :parse_version
30
184
 
31
- # Check if link is supported
185
+ # Detect terminal hyperlink support
186
+ #
187
+ # @example
188
+ # link.link?
189
+ # # => true
32
190
  #
33
191
  # @return [Boolean]
34
192
  #
35
193
  # @api public
36
- def support_link?(output: $stdout)
37
- return false unless output.tty?
194
+ def link?
195
+ return false unless tty?
38
196
 
39
- if ENV["TERM_PROGRAM"] =~ ITERM
40
- version = parse_version(ENV["TERM_PROGRAM_VERSION"])
197
+ terminals.any?(&:link?)
198
+ end
41
199
 
42
- return version[:major] > 3 || version[:major] == 3 && version[:minor] > 0
43
- end
200
+ private
44
201
 
45
- # uses VTE terminal
46
- if ENV["VTE_VERSION"]
47
- version = parse_version(ENV["VTE_VERSION"])
202
+ # Whether to create an {TTY::Link::ANSILink} or a {TTY::Link::PlainLink}
203
+ #
204
+ # @example
205
+ # link.ansi_link?
206
+ # # => true
207
+ #
208
+ # @return [Boolean]
209
+ #
210
+ # @api private
211
+ def ansi_link?
212
+ @hyperlink_parameter.always? || (@hyperlink_parameter.auto? && link?)
213
+ end
48
214
 
49
- return version[:major] > 0 || version[:minor] > 50 ||
50
- version[:minor] == 50 && version[:patch] > 0
51
- end
215
+ # Create an {TTY::Link::ANSILink} instance
216
+ #
217
+ # @example
218
+ # ansi_link("TTY Toolkit", "https://ttytoolkit.org", {id: "tty-tookit"})
219
+ #
220
+ # @param [String] name
221
+ # the URL name
222
+ # @param [String] url
223
+ # the URL target
224
+ # @param [Hash{Symbol => String}] attrs
225
+ # the URL attributes
226
+ #
227
+ # @return [TTY::Link::ANSILink]
228
+ #
229
+ # @see ANSILink#new
230
+ #
231
+ # @api private
232
+ def ansi_link(name, url, attrs)
233
+ ANSILink.new(name, url, attrs)
234
+ end
235
+
236
+ # Read the hyperlink environment variable
237
+ #
238
+ # @example
239
+ # link.hyperlink_env
240
+ # # => "always"
241
+ #
242
+ # @return [String, nil]
243
+ #
244
+ # @api private
245
+ def hyperlink_env
246
+ @env[HYPERLINK_ENV]
247
+ end
52
248
 
53
- return false
249
+ # Create a {TTY::Link::HyperlinkParameter} instance
250
+ #
251
+ # @example
252
+ # link.hyperlink_parameter(:always)
253
+ #
254
+ # @param [String, Symbol] hyperlink
255
+ # the hyperlink detection out of always, auto or never
256
+ #
257
+ # @return [TTY::Link::HyperlinkParameter]
258
+ #
259
+ # @raise [TTY::Link::ValueError]
260
+ # the value isn't always, auto or never
261
+ #
262
+ # @api private
263
+ def hyperlink_parameter(hyperlink)
264
+ HyperlinkParameter.new(hyperlink)
54
265
  end
55
- module_function :support_link?
56
266
 
57
- # Render terminal link
267
+ # Create a {TTY::Link::PlainLink} instance
268
+ #
269
+ # @example
270
+ # plain_link("TTY Toolkit", "https://ttytoolkit.org")
58
271
  #
59
272
  # @param [String] name
273
+ # the URL name
60
274
  # @param [String] url
275
+ # the URL target
61
276
  #
62
- # @return [String]
277
+ # @return [TTY::Link::PlainLink]
63
278
  #
64
- # @api public
65
- def link_to(name, url)
66
- if support_link?
67
- [ OSC, "8", SEP, SEP, url, BEL, name, OSC, "8", SEP, SEP, BEL ].join("")
68
- else
69
- "#{name} -> #{url}"
279
+ # @see PlainLink#new
280
+ #
281
+ # @api private
282
+ def plain_link(name, url)
283
+ PlainLink.new(name, url, @plain)
284
+ end
285
+
286
+ # Terminals for detecting hyperlink support
287
+ #
288
+ # @example
289
+ # link.terminals
290
+ #
291
+ # @return [Array<TTY::Link::Terminals::Abstract>]
292
+ #
293
+ # @api private
294
+ def terminals
295
+ @terminals ||= Terminals.registered.map do |terminal_class|
296
+ terminal_class.new(SemanticVersion, @env)
70
297
  end
71
298
  end
72
- module_function :link_to
299
+
300
+ # Detect the terminal device
301
+ #
302
+ # @example
303
+ # link.tty?
304
+ # # => true
305
+ #
306
+ # @return [Boolean]
307
+ #
308
+ # @api private
309
+ def tty?
310
+ @output.tty?
311
+ end
73
312
  end # Link
74
313
  end # TTY
data/lib/tty-link.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "tty/link"
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty-link
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
- autorequire:
9
- bindir: exe
8
+ autorequire:
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-25 00:00:00.000000000 Z
11
+ date: 2024-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.5.0
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: 1.5.0
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rake
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -42,30 +28,55 @@ dependencies:
42
28
  name: rspec
43
29
  requirement: !ruby/object:Gem::Requirement
44
30
  requirements:
45
- - - "~>"
31
+ - - ">="
46
32
  - !ruby/object:Gem::Version
47
33
  version: '3.0'
48
34
  type: :development
49
35
  prerelease: false
50
36
  version_requirements: !ruby/object:Gem::Requirement
51
37
  requirements:
52
- - - "~>"
38
+ - - ">="
53
39
  - !ruby/object:Gem::Version
54
40
  version: '3.0'
55
- description: Hyperlinks in your terminal
41
+ description: Terminal hyperlinks support detection and generation.
56
42
  email:
57
43
  - piotr@piotrmurach.com
58
44
  executables: []
59
45
  extensions: []
60
46
  extra_rdoc_files:
61
- - README.md
62
47
  - CHANGELOG.md
48
+ - LICENSE.txt
49
+ - README.md
63
50
  files:
64
51
  - CHANGELOG.md
65
52
  - LICENSE.txt
66
53
  - README.md
67
54
  - lib/tty-link.rb
68
55
  - lib/tty/link.rb
56
+ - lib/tty/link/ansi_link.rb
57
+ - lib/tty/link/errors.rb
58
+ - lib/tty/link/hyperlink_parameter.rb
59
+ - lib/tty/link/plain_link.rb
60
+ - lib/tty/link/semantic_version.rb
61
+ - lib/tty/link/terminals.rb
62
+ - lib/tty/link/terminals/abstract.rb
63
+ - lib/tty/link/terminals/alacritty.rb
64
+ - lib/tty/link/terminals/contour.rb
65
+ - lib/tty/link/terminals/domterm.rb
66
+ - lib/tty/link/terminals/foot.rb
67
+ - lib/tty/link/terminals/hyper.rb
68
+ - lib/tty/link/terminals/iterm.rb
69
+ - lib/tty/link/terminals/jediterm.rb
70
+ - lib/tty/link/terminals/kitty.rb
71
+ - lib/tty/link/terminals/konsole.rb
72
+ - lib/tty/link/terminals/mintty.rb
73
+ - lib/tty/link/terminals/rio.rb
74
+ - lib/tty/link/terminals/tabby.rb
75
+ - lib/tty/link/terminals/terminology.rb
76
+ - lib/tty/link/terminals/vscode.rb
77
+ - lib/tty/link/terminals/vte.rb
78
+ - lib/tty/link/terminals/wezterm.rb
79
+ - lib/tty/link/terminals/wt.rb
69
80
  - lib/tty/link/version.rb
70
81
  homepage: https://ttytoolkit.org
71
82
  licenses:
@@ -75,9 +86,11 @@ metadata:
75
86
  bug_tracker_uri: https://github.com/piotrmurach/tty-link/issues
76
87
  changelog_uri: https://github.com/piotrmurach/tty-link/blob/master/CHANGELOG.md
77
88
  documentation_uri: https://www.rubydoc.info/gems/tty-link
89
+ funding_uri: https://github.com/sponsors/piotrmurach
78
90
  homepage_uri: https://ttytoolkit.org
91
+ rubygems_mfa_required: 'true'
79
92
  source_code_uri: https://github.com/piotrmurach/tty-link
80
- post_install_message:
93
+ post_install_message:
81
94
  rdoc_options: []
82
95
  require_paths:
83
96
  - lib
@@ -92,8 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
105
  - !ruby/object:Gem::Version
93
106
  version: '0'
94
107
  requirements: []
95
- rubygems_version: 3.1.2
96
- signing_key:
108
+ rubygems_version: 3.4.10
109
+ signing_key:
97
110
  specification_version: 4
98
- summary: Hyperlinks in your terminal
111
+ summary: Terminal hyperlinks support detection and generation.
99
112
  test_files: []