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,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTY
4
+ class Link
5
+ # Responsible for converting a URL to a plain terminal link
6
+ #
7
+ # @api private
8
+ class PlainLink
9
+ # The replacement tokens pattern
10
+ #
11
+ # @return [Regexp]
12
+ #
13
+ # @api private
14
+ REPLACEMENT_TOKENS_PATTERN = /:(name|url)/i.freeze
15
+ private_constant :REPLACEMENT_TOKENS_PATTERN
16
+
17
+ # Create a {TTY::Link::PlainLink} instance
18
+ #
19
+ # @example
20
+ # plain_link = TTY::Link::PlainLink.new(
21
+ # "TTY Toolkit", "https://ttytoolkit.org", ":name (:url)")
22
+ #
23
+ # @param [String] name
24
+ # the URL name
25
+ # @param [String] url
26
+ # the URL target
27
+ # @param [String] template
28
+ # the URL replacement template
29
+ #
30
+ # @api public
31
+ def initialize(name, url, template)
32
+ @name = name
33
+ @url = url
34
+ @template = template
35
+ end
36
+
37
+ # Convert this link to a plain string
38
+ #
39
+ # @example
40
+ # plain_link.to_s
41
+ # # => "TTY Toolkit (https://ttytoolkit.org)"
42
+ #
43
+ # @return [String]
44
+ #
45
+ # @api public
46
+ def to_s
47
+ replacements = {":name" => @name, ":url" => @url}
48
+ @template.gsub(REPLACEMENT_TOKENS_PATTERN, replacements)
49
+ end
50
+ end # PlainLink
51
+ end # Link
52
+ end # TTY
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTY
4
+ class Link
5
+ # Responsible for comparing terminal release versions
6
+ #
7
+ # @api private
8
+ class SemanticVersion
9
+ include Comparable
10
+
11
+ # The unseparated version pattern
12
+ #
13
+ # @return [Regexp]
14
+ #
15
+ # @api private
16
+ UNSEPARATED_VERSION_PATTERN = /^(\d{1,2})(\d{2})$/.freeze
17
+ private_constant :UNSEPARATED_VERSION_PATTERN
18
+
19
+ # The version separator
20
+ #
21
+ # @return [String]
22
+ #
23
+ # @api private
24
+ VERSION_SEPARATOR = "."
25
+ private_constant :VERSION_SEPARATOR
26
+
27
+ # The zero number
28
+ #
29
+ # @return [String]
30
+ #
31
+ # @api private
32
+ ZERO_NUMBER = "0"
33
+ private_constant :ZERO_NUMBER
34
+
35
+ # Create a {TTY::Link::SemanticVersion} instance from a version value
36
+ #
37
+ # @example
38
+ # TTY::Link::SemanticVersion.from(1, 2, 3)
39
+ #
40
+ # @example
41
+ # TTY::Link::SemanticVersion[1, 2, 3]
42
+ #
43
+ # @example
44
+ # TTY::Link::SemanticVersion.from("1234")
45
+ #
46
+ # @example
47
+ # TTY::Link::SemanticVersion.from("1.2.3")
48
+ #
49
+ # @example
50
+ # TTY::Link::SemanticVersion.from("1-2-3", separator: "-")
51
+ #
52
+ # @param [Array<Integer, String>] version
53
+ # the version to convert to a semantic version
54
+ # @param [String] separator
55
+ # the version separator
56
+ #
57
+ # @return [TTY::Link::SemanticVersion]
58
+ #
59
+ # @api public
60
+ def self.from(*version, separator: VERSION_SEPARATOR)
61
+ major, minor, patch =
62
+ if version.size == 1 && version[0].respond_to?(:split)
63
+ convert_to_array(version[0], separator: separator)
64
+ else
65
+ version
66
+ end
67
+ new(major.to_i, minor.to_i, patch.to_i)
68
+ end
69
+ singleton_class.send(:alias_method, :[], :from)
70
+
71
+ # Convert a string version to an array
72
+ #
73
+ # @example
74
+ # TTY::Link::SemanticVersion.from("1234")
75
+ # # => ["0", "12", "34"]
76
+ #
77
+ # @example
78
+ # TTY::Link::SemanticVersion.convert_to_array("1.2.3")
79
+ # # => ["1", "2", "3"]
80
+ #
81
+ # @example
82
+ # TTY::Link::SemanticVersion.convert_to_array("1-2-3", separator: "-")
83
+ # # => ["1", "2", "3"]
84
+ #
85
+ # @param [String] version
86
+ # the version to convert to an array
87
+ # @param [String] separator
88
+ # the version separator
89
+ #
90
+ # @return [Array<String>]
91
+ #
92
+ # @api private
93
+ def self.convert_to_array(version, separator: VERSION_SEPARATOR)
94
+ if (matches = version.match(UNSEPARATED_VERSION_PATTERN))
95
+ [ZERO_NUMBER, matches[1], matches[2]]
96
+ else
97
+ version.split(separator)
98
+ end
99
+ end
100
+ private_class_method :convert_to_array
101
+
102
+ # The major number
103
+ #
104
+ # @example
105
+ # semantic_version.major
106
+ #
107
+ # @return [Integer]
108
+ #
109
+ # @api public
110
+ attr_reader :major
111
+
112
+ # The minor number
113
+ #
114
+ # @example
115
+ # semantic_version.minor
116
+ #
117
+ # @return [Integer]
118
+ #
119
+ # @api public
120
+ attr_reader :minor
121
+
122
+ # The patch number
123
+ #
124
+ # @example
125
+ # semantic_version.patch
126
+ #
127
+ # @return [Integer]
128
+ #
129
+ # @api public
130
+ attr_reader :patch
131
+
132
+ # Create a {TTY::Link::SemanticVersion} instance
133
+ #
134
+ # @example
135
+ # TTY::Link::SemanticVersion.new(1, 2, 3)
136
+ #
137
+ # @param [Integer] major
138
+ # the major number
139
+ # @param [Integer] minor
140
+ # the minor number
141
+ # @param [Integer] patch
142
+ # the patch number
143
+ #
144
+ # @api private
145
+ def initialize(major, minor, patch)
146
+ @major = major
147
+ @minor = minor
148
+ @patch = patch
149
+ end
150
+ private_class_method :new
151
+
152
+ # Compare this semantic version with another object
153
+ #
154
+ # @example
155
+ # semantic_version >= other
156
+ #
157
+ # @param [Object] other
158
+ # the other object to compare with
159
+ #
160
+ # @return [Integer, nil]
161
+ # Return negative, zero, or positive number when
162
+ # this semantic version is less than, equal to, or
163
+ # greater than other semantic version. Return nil
164
+ # when the other object is not a semantic version.
165
+ #
166
+ # @api public
167
+ def <=>(other)
168
+ return unless other.is_a?(self.class)
169
+
170
+ major_comparison = @major <=> other.major
171
+ return major_comparison unless major_comparison.zero?
172
+
173
+ minor_comparison = @minor <=> other.minor
174
+ return minor_comparison unless minor_comparison.zero?
175
+
176
+ @patch <=> other.patch
177
+ end
178
+
179
+ # Generate hash value for this semantic version
180
+ #
181
+ # @example
182
+ # semantic_version.hash
183
+ #
184
+ # @return [Integer]
185
+ #
186
+ # @api public
187
+ def hash
188
+ [self.class, @major, @minor, @patch].hash
189
+ end
190
+
191
+ # Convert this semantic version to a string
192
+ #
193
+ # @example
194
+ # semantic_version.inspect
195
+ #
196
+ # @return [String]
197
+ #
198
+ # @api public
199
+ def inspect
200
+ [@major, @minor, @patch].join(VERSION_SEPARATOR)
201
+ end
202
+ end # SemanticVersion
203
+ end # Link
204
+ end # TTY
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../errors"
4
+
5
+ module TTY
6
+ class Link
7
+ module Terminals
8
+ # Responsible for providing common terminal detection
9
+ #
10
+ # @abstract Override {#name?} and {#version?} to implement
11
+ # terminal hyperlinks detection
12
+ #
13
+ # @api private
14
+ class Abstract
15
+ # The term environment variable name
16
+ #
17
+ # @return [String]
18
+ #
19
+ # @api private
20
+ TERM = "TERM"
21
+ private_constant :TERM
22
+
23
+ # The term program environment variable name
24
+ #
25
+ # @return [String]
26
+ #
27
+ # @api private
28
+ TERM_PROGRAM = "TERM_PROGRAM"
29
+ private_constant :TERM_PROGRAM
30
+
31
+ # The term program version environment variable name
32
+ #
33
+ # @return [String]
34
+ #
35
+ # @api private
36
+ TERM_PROGRAM_VERSION = "TERM_PROGRAM_VERSION"
37
+ private_constant :TERM_PROGRAM_VERSION
38
+
39
+ # Register a terminal class with terminals
40
+ #
41
+ # @param [TTY::Link::Terminal::Abstract] terminal_class
42
+ # the terminal class to register
43
+ #
44
+ # @return [void]
45
+ #
46
+ # @api private
47
+ def self.inherited(terminal_class)
48
+ super
49
+ Terminals.register(terminal_class)
50
+ end
51
+ private_class_method :inherited
52
+
53
+ # Create an {TTY::Link::Terminals::Abstract} instance
54
+ #
55
+ # @example
56
+ # terminal = TTY::Link::Terminals::Abstract.new(SemanticVersion, ENV)
57
+ #
58
+ # @param [TTY::Link::SemanticVersion] semantic_version
59
+ # the semantic version creator
60
+ # @param [ENV, Hash{String => String}] env
61
+ # the environment variables
62
+ #
63
+ # @api public
64
+ def initialize(semantic_version, env)
65
+ @semantic_version = semantic_version
66
+ @env = env
67
+ end
68
+
69
+ # Detect a terminal hyperlink support
70
+ #
71
+ # @example
72
+ # terminal.link?
73
+ # # => true
74
+ #
75
+ # @return [Boolean]
76
+ #
77
+ # @api public
78
+ def link?
79
+ name? && version?
80
+ end
81
+
82
+ protected
83
+
84
+ # Detect a terminal name
85
+ #
86
+ # @example
87
+ # terminal.name?
88
+ #
89
+ # @raise [TTY::Link::AbstractMethodError]
90
+ # the class doesn't implement the name? method
91
+ #
92
+ # @abstract
93
+ #
94
+ # @api private
95
+ def name?
96
+ raise AbstractMethodError.new(self.class.name, __method__)
97
+ end
98
+
99
+ # Detect whether a terminal version supports terminal hyperlinks
100
+ #
101
+ # @example
102
+ # terminal.version?
103
+ #
104
+ # @raise [TTY::Link::AbstractMethodError]
105
+ # the class doesn't implement the version? method
106
+ #
107
+ # @abstract
108
+ #
109
+ # @api private
110
+ def version?
111
+ raise AbstractMethodError.new(self.class.name, __method__)
112
+ end
113
+
114
+ # The environment variables
115
+ #
116
+ # @example
117
+ # terminal.env
118
+ #
119
+ # @return [ENV, Hash{String => String}]
120
+ #
121
+ # @api private
122
+ attr_reader :env
123
+
124
+ # Create a {TTY::Link::SemanticVersion} instance from a version value
125
+ #
126
+ # @example
127
+ # terminal.semantic_version(1, 2, 3)
128
+ #
129
+ # @example
130
+ # terminal.semantic_version("1.2.3")
131
+ #
132
+ # @param [Array<Integer, String>] version
133
+ # the version to convert to a semantic version
134
+ # @param [Hash{Symbol => String}] options
135
+ # the options to convert to a semantic version
136
+ # @option options [String] :separator
137
+ # the version separator
138
+ #
139
+ # @return [TTY::Link::SemanticVersion]
140
+ #
141
+ # @see SemanticVersion#from
142
+ #
143
+ # @api private
144
+ def semantic_version(*version, **options)
145
+ @semantic_version.from(*version, **options)
146
+ end
147
+
148
+ # Read the term environment variable
149
+ #
150
+ # @example
151
+ # terminal.term
152
+ # # => "alacritty"
153
+ #
154
+ # @return [String, nil]
155
+ #
156
+ # @api private
157
+ def term
158
+ env[TERM]
159
+ end
160
+
161
+ # Read the term program environment variable
162
+ #
163
+ # @example
164
+ # terminal.term_program
165
+ # # => "iTerm.app"
166
+ #
167
+ # @return [String, nil]
168
+ #
169
+ # @api private
170
+ def term_program
171
+ env[TERM_PROGRAM]
172
+ end
173
+
174
+ # Read the term program version environment variable
175
+ #
176
+ # @example
177
+ # terminal.term_program_version
178
+ # # => "1.2.3"
179
+ #
180
+ # @return [String, nil]
181
+ #
182
+ # @api private
183
+ def term_program_version
184
+ env[TERM_PROGRAM_VERSION]
185
+ end
186
+ end # Abstract
187
+ end # Terminals
188
+ end # Link
189
+ end # TTY
@@ -0,0 +1,50 @@
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 Alacritty terminal
9
+ #
10
+ # @api private
11
+ class Alacritty < Abstract
12
+ # The Alacritty terminal name pattern
13
+ #
14
+ # @return [Regexp]
15
+ #
16
+ # @api private
17
+ ALACRITTY = /alacritty/i.freeze
18
+ private_constant :ALACRITTY
19
+
20
+ private
21
+
22
+ # Detect Alacritty terminal
23
+ #
24
+ # @example
25
+ # alacritty.name?
26
+ # # => true
27
+ #
28
+ # @return [Boolean]
29
+ #
30
+ # @api private
31
+ def name?
32
+ !(term =~ ALACRITTY).nil?
33
+ end
34
+
35
+ # Detect any Alacritty version to support terminal hyperlinks
36
+ #
37
+ # @example
38
+ # alacritty.version?
39
+ # # => true
40
+ #
41
+ # @return [Boolean]
42
+ #
43
+ # @api private
44
+ def version?
45
+ true
46
+ end
47
+ end # Alacritty
48
+ end # Terminals
49
+ end # Link
50
+ end # TTY
@@ -0,0 +1,96 @@
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 Contour terminal
9
+ #
10
+ # @api private
11
+ class Contour < Abstract
12
+ # The Contour terminal name pattern
13
+ #
14
+ # @return [Regexp]
15
+ #
16
+ # @api private
17
+ CONTOUR = /contour/i.freeze
18
+ private_constant :CONTOUR
19
+
20
+ # The terminal name environment variable name
21
+ #
22
+ # @return [String]
23
+ #
24
+ # @api private
25
+ TERMINAL_NAME = "TERMINAL_NAME"
26
+ private_constant :TERMINAL_NAME
27
+
28
+ # The terminal version triple environment variable name
29
+ #
30
+ # @return [String]
31
+ #
32
+ # @api private
33
+ TERMINAL_VERSION_TRIPLE = "TERMINAL_VERSION_TRIPLE"
34
+ private_constant :TERMINAL_VERSION_TRIPLE
35
+
36
+ private
37
+
38
+ # Detect Contour terminal
39
+ #
40
+ # @example
41
+ # contour.name?
42
+ # # => true
43
+ #
44
+ # @return [Boolean]
45
+ #
46
+ # @api private
47
+ def name?
48
+ !(terminal_name =~ CONTOUR).nil?
49
+ end
50
+
51
+ # Detect whether the Contour version supports terminal hyperlinks
52
+ #
53
+ # @example
54
+ # contour.version?
55
+ # # => true
56
+ #
57
+ # @return [Boolean]
58
+ #
59
+ # @api private
60
+ def version?
61
+ return false unless terminal_version_triple
62
+
63
+ current_semantic_version = semantic_version(terminal_version_triple)
64
+
65
+ current_semantic_version >= semantic_version(0, 1, 0)
66
+ end
67
+
68
+ # Read the terminal name environment variable
69
+ #
70
+ # @example
71
+ # contour.terminal_name
72
+ # # => "contour"
73
+ #
74
+ # @return [String, nil]
75
+ #
76
+ # @api private
77
+ def terminal_name
78
+ env[TERMINAL_NAME]
79
+ end
80
+
81
+ # Read the terminal version triple environment variable
82
+ #
83
+ # @example
84
+ # contour.terminal_version_triple
85
+ # # => "1.2.3"
86
+ #
87
+ # @return [String, nil]
88
+ #
89
+ # @api private
90
+ def terminal_version_triple
91
+ env[TERMINAL_VERSION_TRIPLE]
92
+ end
93
+ end # Contour
94
+ end # Terminals
95
+ end # Link
96
+ end # TTY
@@ -0,0 +1,107 @@
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 DomTerm terminal
9
+ #
10
+ # @api private
11
+ class Domterm < Abstract
12
+ # The domterm environment variable name
13
+ #
14
+ # @return [String]
15
+ #
16
+ # @api private
17
+ DOMTERM = "DOMTERM"
18
+ private_constant :DOMTERM
19
+
20
+ # The key and value separator
21
+ #
22
+ # @return [String]
23
+ #
24
+ # @api private
25
+ KEY_VAL_SEP = "="
26
+ private_constant :KEY_VAL_SEP
27
+
28
+ # The parameter separator
29
+ #
30
+ # @return [String]
31
+ #
32
+ # @api private
33
+ PARAM_SEP = ";"
34
+ private_constant :PARAM_SEP
35
+
36
+ # The version parameter pattern
37
+ #
38
+ # @return [Regexp]
39
+ #
40
+ # @api private
41
+ VERSION_PARAM = /version/i.freeze
42
+ private_constant :VERSION_PARAM
43
+
44
+ private
45
+
46
+ # Detect DomTerm terminal
47
+ #
48
+ # @example
49
+ # domterm.name?
50
+ # # => true
51
+ #
52
+ # @return [Boolean]
53
+ #
54
+ # @api private
55
+ def name?
56
+ !domterm.nil?
57
+ end
58
+
59
+ # Detect whether the DomTerm version supports terminal hyperlinks
60
+ #
61
+ # @example
62
+ # domterm.version?
63
+ # # => true
64
+ #
65
+ # @return [Boolean]
66
+ #
67
+ # @api private
68
+ def version?
69
+ return false unless domterm_version
70
+
71
+ current_semantic_version = semantic_version(domterm_version)
72
+
73
+ current_semantic_version >= semantic_version(1, 0, 2)
74
+ end
75
+
76
+ # Read the domterm environment variable
77
+ #
78
+ # @example
79
+ # domterm.domterm
80
+ # # => "version=1.2.3;tty=/dev/pts/1"
81
+ #
82
+ # @return [String, nil]
83
+ #
84
+ # @api private
85
+ def domterm
86
+ env[DOMTERM]
87
+ end
88
+
89
+ # Read the version from the domterm environment variable
90
+ #
91
+ # @example
92
+ # domterm.domterm_version
93
+ # # => "1.2.3"
94
+ #
95
+ # @return [String, nil]
96
+ #
97
+ # @api private
98
+ def domterm_version
99
+ version_pair = domterm.split(PARAM_SEP).grep(VERSION_PARAM)[0]
100
+ return unless version_pair
101
+
102
+ version_pair.split(KEY_VAL_SEP)[1]
103
+ end
104
+ end # Domterm
105
+ end # Terminals
106
+ end # Link
107
+ end # TTY