searchlink 2.3.68 → 2.3.70

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,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # title: Script plugin
4
+ # description: Load custom searches from non-ruby scripts
5
+ module SL
6
+ # Script Search
7
+ class ScriptSearch
8
+ def initialize(config)
9
+ @filename = config['filename']
10
+ @path = config['path']
11
+
12
+ %w[trigger searches name script].each do |key|
13
+ raise PluginError.new(%(configuration missing key "#{key}"), plugin: @filename) unless config.key?(key)
14
+ end
15
+
16
+ @trigger = config['trigger']
17
+ @searches = config['searches']
18
+ @name = config['name']
19
+ @script = find_script(config['script'])
20
+
21
+ unless File.executable?(@script)
22
+ raise PluginError.new(%(script "#{File.basename(@script)}" not executable\nrun `chmod a+x #{@script.shorten_path}` to correct),
23
+ plugin: @filename)
24
+ end
25
+
26
+ class << self
27
+ def settings
28
+ {
29
+ trigger: @trigger,
30
+ searches: @searches
31
+ }
32
+ end
33
+
34
+ def search(search_type, search_terms, link_text)
35
+ type = Shellwords.escape(search_type)
36
+ terms = Shellwords.escape(search_terms)
37
+ text = Shellwords.escape(link_text)
38
+
39
+ stdout = `#{[@script, type, terms, text].join(' ')} 2>&1`
40
+
41
+ unless $CHILD_STATUS.success?
42
+ raise PluginError.new(%("#{File.basename(@script)}" returned error #{$CHILD_STATUS.exitstatus}\n#{stdout}),
43
+ plugin: @filename)
44
+ end
45
+
46
+ begin
47
+ res = JSON.parse(stdout)
48
+ rescue JSON::ParserError
49
+ res = YAML.safe_load(stdout)
50
+ end
51
+
52
+ unless res.is_a?(Hash)
53
+ raise PluginError.new(%(invalid results from "#{File.basename(@script)}", must be YAML or JSON string),
54
+ plugin: @filename)
55
+ end
56
+
57
+ %w[url title link_text].each do |key|
58
+ unless res.key?(key)
59
+ raise PluginError.new(%("#{File.basename(@script)}" output missing key "#{key}"), plugin: @filename)
60
+ end
61
+ end
62
+
63
+ [res['url'], res['title'], res['link_text']]
64
+ end
65
+ end
66
+
67
+ SL::Searches.register @name, :search, self
68
+ end
69
+
70
+ def find_script(script)
71
+ return script if File.exist?(File.expand_path(script))
72
+
73
+ base = File.expand_path('~/.config/searchlink/plugins')
74
+ first = File.join(base, script)
75
+ return first if File.exist?(first)
76
+
77
+ base = File.expand_path('~/.config/searchlink')
78
+ second = File.join(base, script)
79
+ return second if File.exist?(second)
80
+
81
+ raise PluginError.new(%(Script plugin script "#{script}" not found), plugin: @filename)
82
+ end
83
+ end
84
+ end
@@ -36,7 +36,7 @@ module SL
36
36
  def search(search_type, search_terms, link_text)
37
37
  return zero_click(search_terms, link_text) if search_type =~ /^z$/
38
38
 
39
- # return SL.ddg(search_terms, link_text) if search_type == 'g' && SL::GoogleSearch.test_for_key
39
+ # return SL.ddg(search_terms, link_text) if search_type == 'g' && SL::GoogleSearch.api_key?
40
40
 
41
41
  terms = "%5C#{search_terms.url_encode}"
42
42
  page = Curl::Html.new("https://duckduckgo.com/?q=#{terms}", compressed: true)
@@ -122,7 +122,7 @@ module SL
122
122
  # @param timeout [Integer] The timeout
123
123
  #
124
124
  def google(search_terms, link_text = nil, timeout: SL.config['timeout'], image: false)
125
- if SL::GoogleSearch.test_for_key
125
+ if SL::GoogleSearch.api_key?
126
126
  s_class = 'google'
127
127
  s_type = image ? 'img' : 'gg'
128
128
  else
@@ -147,7 +147,7 @@ module SL
147
147
  # @return [SL::Searches::Result] The search result
148
148
  #
149
149
  def ddg(search_terms, link_text = nil, timeout: SL.config['timeout'], google: true, image: false)
150
- if google && SL::GoogleSearch.test_for_key
150
+ if google && SL::GoogleSearch.api_key?
151
151
  s_class = 'google'
152
152
  s_type = image ? 'img' : 'gg'
153
153
  else
@@ -16,7 +16,7 @@ module SL
16
16
  }
17
17
  end
18
18
 
19
- def test_for_key
19
+ def api_key?
20
20
  return false unless SL.config.key?('google_api_key') && SL.config['google_api_key']
21
21
 
22
22
  key = SL.config['google_api_key']
@@ -30,7 +30,7 @@ module SL
30
30
  def search(search_type, search_terms, link_text)
31
31
  image = search_type =~ /img$/ ? true : false
32
32
 
33
- unless test_for_key
33
+ unless api_key?
34
34
  SL.add_error('api key', 'Missing Google API Key')
35
35
  return false
36
36
  end
@@ -31,14 +31,25 @@ module SL
31
31
  ## @return [Array] Single bookmark, [url, title, date]
32
32
  ##
33
33
  def search_brave_history(term)
34
- # Google history
35
- history_file = File.expand_path('~/Library/Application Support/BraveSoftware/Brave-Browser/Default/History')
36
- if File.exist?(history_file)
37
- SL.notify('Searching Brave History', term)
38
- search_chromium_history(history_file, term)
39
- else
40
- false
34
+ base = File.expand_path('~/Library/Application Support/BraveSoftware/Brave-Browser/')
35
+ profiles = Dir.glob('**/History', base: base)
36
+ profiles.delete_if { |p| p =~ /^Snapshots/ }
37
+ profiles.map! { |f| File.join(base, f) }
38
+
39
+ res = false
40
+
41
+ profiles.each do |bookmarks|
42
+ next unless File.exist?(bookmarks)
43
+
44
+ profile = bookmarks.match(%r{Browser/([^/]+)/})[1]
45
+
46
+ SL.notify("Searching Brave History for profile #{profile}", term)
47
+ res = search_chromium_history(bookmarks, term)
48
+
49
+ break if res
41
50
  end
51
+
52
+ res
42
53
  end
43
54
 
44
55
  ## Search Edge history
@@ -48,14 +59,25 @@ module SL
48
59
  ## @return [Array] Single bookmark, [url, title, date]
49
60
  ##
50
61
  def search_edge_history(term)
51
- # Google history
52
- history_file = File.expand_path('~/Library/Application Support/Microsoft Edge/Default/History')
53
- if File.exist?(history_file)
54
- SL.notify('Searching Edge History', term)
55
- search_chromium_history(history_file, term)
56
- else
57
- false
62
+ base = File.expand_path('~/Library/Application Support/Microsoft Edge/')
63
+ profiles = Dir.glob('**/History', base: base)
64
+ profiles.delete_if { |p| p =~ /^Snapshots/ }
65
+ profiles.map! { |f| File.join(base, f) }
66
+
67
+ res = false
68
+
69
+ profiles.each do |bookmarks|
70
+ next unless File.exist?(bookmarks)
71
+
72
+ profile = bookmarks.match(%r{Edge/([^/]+)/})[1]
73
+
74
+ SL.notify("Searching Chrome History for profile #{profile}", term)
75
+ res = search_chromium_history(bookmarks, term)
76
+
77
+ break if res
58
78
  end
79
+
80
+ res
59
81
  end
60
82
 
61
83
  ## Search Chrome history
@@ -66,13 +88,27 @@ module SL
66
88
  ##
67
89
  def search_chrome_history(term)
68
90
  # Google history
69
- history_file = File.expand_path('~/Library/Application Support/Google/Chrome/Default/History')
70
- if File.exist?(history_file)
71
- SL.notify('Searching Chrome History', term)
72
- search_chromium_history(history_file, term)
73
- else
74
- false
91
+ base = File.expand_path('~/Library/Application Support/Google/Chrome/')
92
+ profiles = Dir.glob('**/History', base: base)
93
+ profiles.delete_if { |p| p =~ /^Snapshots/ }
94
+ profiles.map! { |f| File.join(base, f) }
95
+
96
+ # bookmarks_file = File.expand_path('~/Library/Application Support/Google/Chrome/Default/Bookmarks')
97
+
98
+ res = false
99
+
100
+ profiles.each do |bookmarks|
101
+ next unless File.exist?(bookmarks)
102
+
103
+ profile = bookmarks.match(%r{Chrome/([^/]+)/})[1]
104
+
105
+ SL.notify("Searching Chrome History for profile #{profile}", term)
106
+ res = search_chromium_history(bookmarks, term)
107
+
108
+ break if res
75
109
  end
110
+
111
+ res
76
112
  end
77
113
 
78
114
  ##
@@ -105,6 +141,7 @@ module SL
105
141
  terms.push("(url NOT LIKE '%search/?%'
106
142
  AND url NOT LIKE '%?q=%'
107
143
  AND url NOT LIKE '%?s=%'
144
+ AND url NOT LIKE '%www.bing.com%'
108
145
  AND url NOT LIKE '%duckduckgo.com/?t%')")
109
146
  if exact_match
110
147
  terms.push("(url LIKE '%#{term.strip.downcase}%' OR title LIKE '%#{term.strip.downcase}%')")
@@ -138,11 +175,11 @@ module SL
138
175
  ## @return [Array] single bookmark [url, title, date]
139
176
  ##
140
177
  def search_arc_bookmarks(term)
141
- bookmarks_file = File.expand_path('~/Library/Application Support/Arc/User Data/Default/Bookmarks')
178
+ bookmarks_file = File.expand_path('~/Library/Application Support/Arc/StorableSidebar.json')
142
179
 
143
180
  if File.exist?(bookmarks_file)
144
- SL.notify('Searching Brave Bookmarks', term)
145
- return search_chromium_bookmarks(bookmarks_file, term)
181
+ SL.notify('Searching Arc Bookmarks', term)
182
+ return search_arc_json(bookmarks_file, term)
146
183
  end
147
184
 
148
185
  false
@@ -156,14 +193,25 @@ module SL
156
193
  ## @return [Array] single bookmark [url, title, date]
157
194
  ##
158
195
  def search_brave_bookmarks(term)
159
- bookmarks_file = File.expand_path('~/Library/Application Support/BraveSoftware/Brave-Browser/Default/Bookmarks')
196
+ base = File.expand_path('~/Library/Application Support/BraveSoftware/Brave-Browser/')
197
+ profiles = Dir.glob('**/Bookmarks', base: base)
198
+ profiles.delete_if { |p| p =~ /^Snapshots/ }
199
+ profiles.map! { |f| File.join(base, f) }
160
200
 
161
- if File.exist?(bookmarks_file)
162
- SL.notify('Searching Brave Bookmarks', term)
163
- return search_chromium_bookmarks(bookmarks_file, term)
201
+ res = false
202
+
203
+ profiles.each do |bookmarks|
204
+ next unless File.exist?(bookmarks)
205
+
206
+ profile = bookmarks.match(%r{Browser/([^/]+)/})[1]
207
+
208
+ SL.notify("Searching Brave Bookmarks for profile #{profile}", term)
209
+ res = search_chromium_bookmarks(bookmarks, term)
210
+
211
+ break if res
164
212
  end
165
213
 
166
- false
214
+ res
167
215
  end
168
216
 
169
217
  ##
@@ -174,14 +222,24 @@ module SL
174
222
  ## @return [Array] single bookmark [url, title, date]
175
223
  ##
176
224
  def search_edge_bookmarks(term)
177
- bookmarks_file = File.expand_path('~/Library/Application Support/Microsoft Edge/Default/Bookmarks')
225
+ base = File.expand_path('~/Library/Application Support/Microsoft Edge')
226
+ profiles = Dir.glob('**/Bookmarks', base: base)
227
+ profiles.delete_if { |p| p =~ /^Snapshots/ }
228
+ profiles.map! { |f| File.join(base, f) }
178
229
 
179
- if File.exist?(bookmarks_file)
180
- SL.notify('Searching Edge Bookmarks', term)
181
- return search_chromium_bookmarks(bookmarks_file, term)
230
+ res = false
231
+
232
+ profiles.each do |bookmarks|
233
+ next unless File.exist?(bookmarks)
234
+
235
+ profile = bookmarks.match(%r{Edge/([^/]+)/})[1]
236
+
237
+ SL.notify("Searching Edge Bookmarks for profile #{profile}", term)
238
+ res = search_chromium_bookmarks(bookmarks, term)
239
+ break if res
182
240
  end
183
241
 
184
- false
242
+ res
185
243
  end
186
244
 
187
245
  ##
@@ -192,11 +250,102 @@ module SL
192
250
  ## @return [Array] single bookmark [url, title, date]
193
251
  ##
194
252
  def search_chrome_bookmarks(term)
195
- bookmarks_file = File.expand_path('~/Library/Application Support/Google/Chrome/Default/Bookmarks')
253
+ base = File.expand_path('~/Library/Application Support/Google/Chrome/')
254
+ profiles = Dir.glob('**/Bookmarks', base: base)
255
+ profiles.delete_if { |p| p =~ /^Snapshots/ }
256
+ profiles.map! { |f| File.join(base, f) }
196
257
 
197
- if File.exist?(bookmarks_file)
198
- SL.notify('Searching Chrome Bookmarks', term)
199
- return search_chromium_bookmarks(bookmarks_file, term)
258
+ res = false
259
+
260
+ profiles.each do |bookmarks|
261
+ if File.exist?(bookmarks)
262
+ profile = bookmarks.match(%r{Chrome/([^/]+)/})[1]
263
+
264
+ SL.notify("Searching Chrome Bookmarks for profile #{profile}", term)
265
+ res = search_chromium_bookmarks(bookmarks, term)
266
+ break if res
267
+ end
268
+ end
269
+
270
+ res
271
+ end
272
+
273
+ ##
274
+ ## Search Arc/JSON bookmarks
275
+ ##
276
+ ## @param bookmarks_file [String] path to bookmarks file
277
+ ## @param term [String] the string to search for
278
+ ##
279
+ ## @return [Array] single bookmark [url, title, date]
280
+ ##
281
+ def search_arc_json(bookmarks_file, term)
282
+ arc_bookmarks = JSON.parse(IO.read(bookmarks_file))
283
+
284
+ exact_match = false
285
+ match_phrases = []
286
+
287
+ # If search terms start with ''term, only search for exact string matches
288
+ if term =~ /^ *'/
289
+ exact_match = true
290
+ term.gsub!(/(^ *'+|'+ *$)/, '')
291
+ elsif term =~ /%22(.*?)%22/
292
+ match_phrases = term.scan(/%22(\S.*?\S)%22/)
293
+ term.gsub!(/%22(\S.*?\S)%22/, '')
294
+ end
295
+
296
+ if arc_bookmarks
297
+ bookmarks = []
298
+ arc_bookmarks['sidebarSyncState']['items'].each do |mark|
299
+ next if mark.is_a?(String)
300
+
301
+ next unless mark['value']['childrenIds'].empty?
302
+
303
+ next unless mark['value']['data']['tab']
304
+
305
+ url = {
306
+ url: mark['value']['data']['tab']['savedURL'],
307
+ saved_title: mark['value']['data']['tab']['savedTitle'],
308
+ title: mark['value']['title'],
309
+ created: mark['value']['createdAt'].to_datetime,
310
+ active: mark['value']['data']['tab']['timeLastActiveAt']&.to_datetime
311
+ }
312
+
313
+ score = score_mark(url, term)
314
+
315
+ if score > 7
316
+ url[:score] = score
317
+ bookmarks << url
318
+ end
319
+ end
320
+
321
+ unless bookmarks.empty?
322
+ if exact_match
323
+ bookmarks.delete_if do |bm|
324
+ !(bm[:url].matches_exact(term) ||
325
+ bm[:title].matches_exact(term) ||
326
+ bm[:saved_title].matches_exact(term))
327
+ end
328
+ end
329
+
330
+ if match_phrases
331
+ match_phrases.map! { |phrase| phrase[0] }
332
+ bookmarks.delete_if do |bm|
333
+ matched = true
334
+ match_phrases.each do |phrase|
335
+ matched = false unless bm[:url].matches_exact(phrase) ||
336
+ bm[:title].matches_exact(phrase) ||
337
+ bm[:saved_title].matches_exact(phrase)
338
+ end
339
+ !matched
340
+ end
341
+ end
342
+
343
+ return false if bookmarks.empty?
344
+
345
+ lastest_bookmark = bookmarks.min_by { |u| u[:created] }
346
+
347
+ return [lastest_bookmark[:url], lastest_bookmark[:title], lastest_bookmark[:date]]
348
+ end
200
349
  end
201
350
 
202
351
  false
@@ -284,7 +433,7 @@ module SL
284
433
  urls << url
285
434
  end
286
435
  else
287
- json.each { |_, v| urls = extract_chrome_bookmarks(v, urls, term) }
436
+ json.each_value { |v| urls = extract_chrome_bookmarks(v, urls, term) }
288
437
  end
289
438
  else
290
439
  return urls
@@ -301,19 +450,27 @@ module SL
301
450
  def score_mark(mark, terms)
302
451
  return 0 unless mark[:url]
303
452
 
304
- score = if mark[:title]&.matches_exact(terms)
305
- 12 + mark[:url].matches_score(terms, start_word: false)
306
- elsif mark[:url].matches_exact(terms)
307
- 11
308
- elsif mark[:title] && mark[:title].matches_score(terms) > 5
309
- mark[:title].matches_score(terms)
310
- elsif mark[:url].matches_score(terms, start_word: false)
311
- mark[:url].matches_score(terms, start_word: false)
312
- else
313
- 0
314
- end
453
+ if mark[:title]&.matches_exact(terms)
454
+ 12 + mark[:url].matches_score(terms, start_word: false)
455
+ elsif mark[:url].matches_exact(terms)
456
+ 11
457
+ elsif (mark[:title] && mark[:title].matches_score(terms) > 5) ||
458
+ (mark[:saved_title] && mark[:saved_title].matches_score(terms) > 5)
459
+
460
+ if mark[:saved_title]
461
+ t = mark[:title]&.matches_score(terms)
462
+ s = mark[:saved_title].matches_score(terms)
463
+ return s if t.nil?
315
464
 
316
- score
465
+ [t, s].max
466
+ else
467
+ mark[:title]&.matches_score(terms)
468
+ end
469
+ elsif mark[:url].matches_score(terms, start_word: false)
470
+ mark[:url].matches_score(terms, start_word: false)
471
+ else
472
+ 0
473
+ end
317
474
  end
318
475
  end
319
476
  end
@@ -28,6 +28,7 @@ module SL
28
28
  terms = []
29
29
  terms.push("(url NOT LIKE '%search/?%'
30
30
  AND url NOT LIKE '%?q=%' AND url NOT LIKE '%?s=%'
31
+ AND url NOT LIKE '%/search?%'
31
32
  AND url NOT LIKE '%duckduckgo.com/?t%')")
32
33
  if exact_match
33
34
  terms.push("(url LIKE '%#{term.strip.downcase}%' OR title LIKE '%#{term.strip.downcase}%')")
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SL
4
+ # Searches and plugin registration
4
5
  module Searches
5
6
  class << self
6
7
  def plugins
@@ -25,7 +26,7 @@ module SL
25
26
 
26
27
  def description_for_search(search_type)
27
28
  description = "#{search_type} search"
28
- plugins[:search].each do |_, plugin|
29
+ plugins[:search].each_value do |plugin|
29
30
  search = plugin[:searches].select { |s| s[0].is_a?(Array) ? s[0].include?(search_type) : s[0] == search_type }
30
31
  unless search.empty?
31
32
  description = search[0][1]
@@ -50,7 +51,11 @@ module SL
50
51
  '<tbody>']
51
52
 
52
53
  searches.each do |s|
53
- out << "<tr><td><code>!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1..-1].join(',')})" : s[0]}</code></td><td>#{s[1]}</td></tr>"
54
+ out << "<tr>
55
+ <td>
56
+ <code>!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1..-1].join(',')})" : s[0]}
57
+ </code>
58
+ </td><td>#{s[1]}</td></tr>"
54
59
  end
55
60
  out.concat(['</tbody>', '</table>']).join("\n")
56
61
  end
@@ -62,7 +67,7 @@ module SL
62
67
  #
63
68
  def available_searches
64
69
  searches = []
65
- plugins[:search].each { |_, plugin| searches.concat(plugin[:searches].delete_if { |s| s[1].nil? }) }
70
+ plugins[:search].each_value { |plugin| searches.concat(plugin[:searches].delete_if { |s| s[1].nil? }) }
66
71
  out = []
67
72
  searches.each do |s|
68
73
  out += "!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1..-1].join(',')})" : s[0]}#{s[0].spacer}#{s[1]}"
@@ -77,8 +82,8 @@ module SL
77
82
 
78
83
  def all_possible_searches
79
84
  searches = []
80
- plugins[:search].each { |_, plugin| plugin[:searches].each { |s| searches.push(s[0]) } }
81
- searches.concat(SL.config['custom_site_searches'].keys)
85
+ plugins[:search].each_value { |plugin| plugin[:searches].each { |s| searches.push(s[0]) } }
86
+ searches.concat(SL.config['custom_site_searches'].keys.sort)
82
87
  end
83
88
 
84
89
  def did_you_mean(term)
@@ -101,11 +106,11 @@ module SL
101
106
  end
102
107
 
103
108
  def register_plugin(title, type, klass)
104
- raise StandardError, "Plugin #{title} has no settings method" unless klass.respond_to? :settings
109
+ raise PluginError.new("Plugin has no settings method", plugin: title) unless klass.respond_to? :settings
105
110
 
106
111
  settings = klass.settings
107
112
 
108
- raise StandardError, "Plugin #{title} has no search method" unless klass.respond_to? :search
113
+ raise PluginError.new("Plugin has no search method", plugin: title) unless klass.respond_to? :search
109
114
 
110
115
  plugins[type] ||= {}
111
116
  plugins[type][title] = {
@@ -123,6 +128,8 @@ module SL
123
128
  Dir.glob(File.join(plugins_folder, '**/*.rb')).sort.each do |plugin|
124
129
  require plugin
125
130
  end
131
+
132
+ load_custom_scripts(plugins_folder)
126
133
  end
127
134
 
128
135
  return unless File.directory?(new_plugins_folder)
@@ -130,6 +137,24 @@ module SL
130
137
  Dir.glob(File.join(new_plugins_folder, '**/*.rb')).sort.each do |plugin|
131
138
  require plugin
132
139
  end
140
+
141
+ load_custom_scripts(new_plugins_folder)
142
+ end
143
+
144
+ def load_custom_scripts(plugins_folder)
145
+ Dir.glob(File.join(plugins_folder, '**/*.{json,yml,yaml}')).each do |file|
146
+ ext = File.extname(file).sub(/^\./, '')
147
+ config = IO.read(file)
148
+ cfg = case ext
149
+ when /^y/i
150
+ YAML.safe_load(config)
151
+ else
152
+ JSON.parse(config)
153
+ end
154
+ cfg['filename'] = File.basename(file)
155
+ cfg['path'] = file.shorten_path
156
+ SL::ScriptSearch.new(cfg)
157
+ end
133
158
  end
134
159
 
135
160
  def do_search(search_type, search_terms, link_text, timeout: SL.config['timeout'])
@@ -5,14 +5,14 @@ module SL
5
5
  class SemVer
6
6
  attr_accessor :maj, :min, :patch, :pre
7
7
 
8
- # Initialize a Semantic Version object
9
- #
10
- # @param version_string [String] a semantic version number
11
- #
12
- # @return [SemVer] SemVer object
13
- #
8
+ ## Initialize a Semantic Version object
9
+ ##
10
+ ## @param version_string [String] a semantic version number
11
+ ##
12
+ ## @return [SemVer] SemVer object
13
+ ##
14
14
  def initialize(version_string)
15
- raise "Invalid semantic version number: #{version_string}" unless version_string.valid_version?
15
+ raise VersionError.new("Invalid semantic version number: #{version_string}") unless version_string.valid_version?
16
16
 
17
17
  @maj, @min, @patch = version_string.split(/\./)
18
18
  @pre = nil
@@ -488,5 +488,13 @@ module SL
488
488
  def code_indent
489
489
  split(/\n/).map { |l| " #{l}" }.join("\n")
490
490
  end
491
+
492
+ ##
493
+ ## Shorten path by adding ~ for home directory
494
+ ##
495
+ def shorten_path
496
+ home_directory = ENV['HOME']
497
+ sub(home_directory, '~')
498
+ end
491
499
  end
492
500
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SL
4
- VERSION = '2.3.68'
4
+ VERSION = '2.3.70'
5
5
  end
6
6
 
7
7
  # Main module
data/lib/searchlink.rb CHANGED
@@ -13,10 +13,17 @@ require 'zlib'
13
13
  require 'time'
14
14
  require 'json'
15
15
  require 'erb'
16
+ require 'english'
16
17
 
17
18
  # import
18
19
  require 'tokens' if File.exist?('lib/tokens.rb')
19
20
 
21
+ # import
22
+ require 'searchlink/exceptions'
23
+
24
+ # import
25
+ require 'searchlink/number'
26
+
20
27
  # import
21
28
  require 'searchlink/util'
22
29
 
@@ -62,4 +69,7 @@ require 'searchlink/output'
62
69
  # import
63
70
  require 'searchlink/which'
64
71
 
72
+ # import
73
+ require 'searchlink/script_plugin'
74
+
65
75
  module Secrets; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchlink
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.68
4
+ version: 2.3.70
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-09 00:00:00.000000000 Z
11
+ date: 2024-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -235,10 +235,13 @@ files:
235
235
  - lib/searchlink/curl.rb
236
236
  - lib/searchlink/curl/html.rb
237
237
  - lib/searchlink/curl/json.rb
238
+ - lib/searchlink/exceptions.rb
238
239
  - lib/searchlink/help.rb
240
+ - lib/searchlink/number.rb
239
241
  - lib/searchlink/output.rb
240
242
  - lib/searchlink/parse.rb
241
243
  - lib/searchlink/plist.rb
244
+ - lib/searchlink/script_plugin.rb
242
245
  - lib/searchlink/search.rb
243
246
  - lib/searchlink/searches.rb
244
247
  - lib/searchlink/searches/amazon.rb