searchlink 2.3.68 → 2.3.70

Sign up to get free protection for your applications and to get access to all the features.
@@ -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