tty-link 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +278 -27
  5. data/lib/tty/link/ansi_link.rb +105 -0
  6. data/lib/tty/link/errors.rb +77 -0
  7. data/lib/tty/link/hyperlink_parameter.rb +95 -0
  8. data/lib/tty/link/plain_link.rb +52 -0
  9. data/lib/tty/link/semantic_version.rb +204 -0
  10. data/lib/tty/link/terminals/abstract.rb +189 -0
  11. data/lib/tty/link/terminals/alacritty.rb +50 -0
  12. data/lib/tty/link/terminals/contour.rb +96 -0
  13. data/lib/tty/link/terminals/domterm.rb +107 -0
  14. data/lib/tty/link/terminals/foot.rb +50 -0
  15. data/lib/tty/link/terminals/hyper.rb +54 -0
  16. data/lib/tty/link/terminals/iterm.rb +54 -0
  17. data/lib/tty/link/terminals/jediterm.rb +71 -0
  18. data/lib/tty/link/terminals/kitty.rb +48 -0
  19. data/lib/tty/link/terminals/konsole.rb +65 -0
  20. data/lib/tty/link/terminals/mintty.rb +54 -0
  21. data/lib/tty/link/terminals/rio.rb +54 -0
  22. data/lib/tty/link/terminals/tabby.rb +50 -0
  23. data/lib/tty/link/terminals/terminology.rb +54 -0
  24. data/lib/tty/link/terminals/vscode.rb +54 -0
  25. data/lib/tty/link/terminals/vte.rb +65 -0
  26. data/lib/tty/link/terminals/wezterm.rb +71 -0
  27. data/lib/tty/link/terminals/wt.rb +63 -0
  28. data/lib/tty/link/terminals.rb +71 -0
  29. data/lib/tty/link/version.rb +2 -2
  30. data/lib/tty/link.rb +279 -40
  31. data/lib/tty-link.rb +2 -0
  32. metadata +44 -44
  33. data/.gitignore +0 -12
  34. data/.rspec +0 -3
  35. data/.travis.yml +0 -23
  36. data/CODE_OF_CONDUCT.md +0 -74
  37. data/Gemfile +0 -12
  38. data/ISSUE_TEMPLATE.md +0 -23
  39. data/PULL_REQUEST_TEMPLATE.md +0 -18
  40. data/Rakefile +0 -8
  41. data/appveyor.yml +0 -28
  42. data/bin/console +0 -14
  43. data/bin/setup +0 -8
  44. data/tasks/console.rake +0 -11
  45. data/tasks/coverage.rake +0 -11
  46. data/tasks/spec.rake +0 -29
  47. data/tty-link.gemspec +0 -36
@@ -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