searchlink 2.3.66 → 2.3.68
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/searchlink/array.rb +6 -2
- data/lib/searchlink/config.rb +2 -0
- data/lib/searchlink/curl/html.rb +4 -4
- data/lib/searchlink/curl/json.rb +9 -7
- data/lib/searchlink/help.rb +3 -1
- data/lib/searchlink/output.rb +3 -1
- data/lib/searchlink/parse.rb +18 -15
- data/lib/searchlink/plist.rb +40 -44
- data/lib/searchlink/search.rb +2 -0
- data/lib/searchlink/searches/amazon.rb +2 -0
- data/lib/searchlink/searches/applemusic.rb +2 -0
- data/lib/searchlink/searches/bitly.rb +2 -0
- data/lib/searchlink/searches/definition.rb +2 -0
- data/lib/searchlink/searches/duckduckgo.rb +21 -21
- data/lib/searchlink/searches/github.rb +8 -1
- data/lib/searchlink/searches/google.rb +2 -0
- data/lib/searchlink/searches/helpers/chromium.rb +4 -2
- data/lib/searchlink/searches/helpers/firefox.rb +5 -4
- data/lib/searchlink/searches/helpers/safari.rb +2 -0
- data/lib/searchlink/searches/history.rb +6 -4
- data/lib/searchlink/searches/hook.rb +2 -0
- data/lib/searchlink/searches/itunes.rb +2 -0
- data/lib/searchlink/searches/lastfm.rb +2 -0
- data/lib/searchlink/searches/lyrics.rb +2 -0
- data/lib/searchlink/searches/pinboard.rb +2 -0
- data/lib/searchlink/searches/social.rb +2 -0
- data/lib/searchlink/searches/software.rb +2 -0
- data/lib/searchlink/searches/spelling.rb +2 -0
- data/lib/searchlink/searches/spotlight.rb +3 -1
- data/lib/searchlink/searches/stackoverflow.rb +2 -0
- data/lib/searchlink/searches/tmdb.rb +2 -0
- data/lib/searchlink/searches/twitter.rb +4 -2
- data/lib/searchlink/searches/wikipedia.rb +2 -0
- data/lib/searchlink/searches/youtube.rb +2 -0
- data/lib/searchlink/searches.rb +5 -3
- data/lib/searchlink/semver.rb +2 -0
- data/lib/searchlink/string.rb +16 -13
- data/lib/searchlink/url.rb +4 -0
- data/lib/searchlink/util.rb +3 -0
- data/lib/searchlink/version.rb +5 -2
- data/lib/searchlink/which.rb +145 -150
- data/lib/searchlink.rb +0 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bb82b5a1651a62b1e08835d66262ba2c7f14a1549645bdfa30f6a182dbfb4f1
|
4
|
+
data.tar.gz: e2e5f2f4585673720b2e54c86b30c73a900f2fe9405ae27668368d62543c4d40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80c086ce264c18182ddad89a8b7c8a677b32d685232d571736e767c3fb4f27bc43451c0ab32ba2e68d5a6d0e4226a7634346833875759994fc921b807678d194
|
7
|
+
data.tar.gz: a00fdb7d2371b187e2a1159bd48cade34fe53f0fcf14dd0b1fbb4948e35a17303aab5ea47779d790769bbe70497897b68ff83015f088a7e480f923be42c3c0f9
|
data/lib/searchlink/array.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Array helpers
|
2
4
|
class ::Array
|
3
|
-
#
|
5
|
+
# This method takes an array and returns the longest element of that array.
|
6
|
+
#
|
7
|
+
# @return [String] Longest string in array
|
4
8
|
#
|
5
|
-
# @return [String] first element among longest elements
|
6
9
|
def longest_element
|
7
10
|
longest_elements[0]
|
8
11
|
end
|
12
|
+
|
9
13
|
# Finds the longest elements and returns an Array
|
10
14
|
#
|
11
15
|
# @return [Array] array of longest elements
|
data/lib/searchlink/config.rb
CHANGED
data/lib/searchlink/curl/html.rb
CHANGED
@@ -375,8 +375,6 @@ module Curl
|
|
375
375
|
images
|
376
376
|
end
|
377
377
|
|
378
|
-
|
379
|
-
|
380
378
|
##
|
381
379
|
## Curls the html for the page
|
382
380
|
##
|
@@ -396,7 +394,9 @@ module Curl
|
|
396
394
|
headers = headers.nil? ? '' : headers.map { |h, v| %(-H "#{h}: #{v}") }.join(' ')
|
397
395
|
compress = compressed ? '--compressed' : ''
|
398
396
|
source = `#{@curl} -#{flags} #{compress} #{headers} '#{url}' 2>/dev/null`
|
399
|
-
|
397
|
+
if source.nil? || source.empty?
|
398
|
+
source = `#{@curl} -#{flags} #{compress} -A "#{agent}" #{headers} '#{url}' 2>/dev/null`
|
399
|
+
end
|
400
400
|
|
401
401
|
return false if source.nil? || source.empty?
|
402
402
|
|
@@ -411,7 +411,7 @@ module Curl
|
|
411
411
|
m = Regexp.last_match
|
412
412
|
headers[m[1]] = m[2]
|
413
413
|
else
|
414
|
-
source = lines[idx
|
414
|
+
source = lines[idx..-1].join("\n")
|
415
415
|
break
|
416
416
|
end
|
417
417
|
end
|
data/lib/searchlink/curl/json.rb
CHANGED
@@ -27,11 +27,11 @@ module Curl
|
|
27
27
|
parts = path.split(/./)
|
28
28
|
target = json
|
29
29
|
parts.each do |part|
|
30
|
-
if part =~ /(?<key>[^\[]+)\[(?<int>\d+)\]/
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
target = if part =~ /(?<key>[^\[]+)\[(?<int>\d+)\]/
|
31
|
+
target[key][int.to_i]
|
32
|
+
else
|
33
|
+
target[part]
|
34
|
+
end
|
35
35
|
end
|
36
36
|
|
37
37
|
target
|
@@ -56,7 +56,9 @@ module Curl
|
|
56
56
|
headers = headers.nil? ? '' : headers.map { |h, v| %(-H "#{h}: #{v}") }.join(' ')
|
57
57
|
compress = compressed ? '--compressed' : ''
|
58
58
|
source = `#{@curl} -#{flags} #{compress} #{headers} '#{url}' 2>/dev/null`
|
59
|
-
|
59
|
+
if source.nil? || source.empty?
|
60
|
+
source = `#{@curl} -#{flags} #{compress} -A "#{agent}" #{headers} '#{url}' 2>/dev/null`
|
61
|
+
end
|
60
62
|
|
61
63
|
return false if source.nil? || source.empty?
|
62
64
|
|
@@ -71,7 +73,7 @@ module Curl
|
|
71
73
|
m = Regexp.last_match
|
72
74
|
headers[m[1]] = m[2]
|
73
75
|
else
|
74
|
-
source = lines[idx
|
76
|
+
source = lines[idx..-1].join("\n")
|
75
77
|
break
|
76
78
|
end
|
77
79
|
end
|
data/lib/searchlink/help.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
class SearchLink
|
3
5
|
def help_css
|
@@ -65,7 +67,7 @@ module SL
|
|
65
67
|
|
66
68
|
if SL.config['custom_site_searches']
|
67
69
|
text += "\n-- [Custom Searches] ----------------------\n"
|
68
|
-
SL.config['custom_site_searches'].sort_by { |l,
|
70
|
+
SL.config['custom_site_searches'].sort_by { |l, _s| l }.each { |label, site| text += "!#{label}#{label.spacer} #{site}\n" }
|
69
71
|
end
|
70
72
|
text
|
71
73
|
end
|
data/lib/searchlink/output.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
class << self
|
3
5
|
attr_writer :titleize, :clipboard, :output, :footer, :line_num,
|
@@ -95,7 +97,7 @@ module SL
|
|
95
97
|
text = text ? text.strip : title
|
96
98
|
title = title && (SL.config['include_titles'] || force_title) ? %( "#{title.clean}") : ''
|
97
99
|
|
98
|
-
title.gsub
|
100
|
+
title = title.gsub(/[ \t]+/, ' ')
|
99
101
|
|
100
102
|
case type.to_sym
|
101
103
|
when :ref_title
|
data/lib/searchlink/parse.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
class SearchLink
|
3
5
|
# Parse arguments in the input string
|
@@ -8,9 +10,9 @@ module SL
|
|
8
10
|
# @option opt [Boolean] :no_restore (false) whether to restore previous config
|
9
11
|
# @return [String] the parsed string
|
10
12
|
#
|
11
|
-
def parse_arguments(string, opt={})
|
13
|
+
def parse_arguments(string, opt = {})
|
12
14
|
input = string.dup
|
13
|
-
return
|
15
|
+
return '' if input.nil?
|
14
16
|
|
15
17
|
skip_flags = opt[:only_meta] || false
|
16
18
|
no_restore = opt[:no_restore] || false
|
@@ -279,7 +281,11 @@ module SL
|
|
279
281
|
if link_info =~ /^(?:[!\^](\S+))\s*(.*)$/
|
280
282
|
m = Regexp.last_match
|
281
283
|
|
282
|
-
search_type = m[1].nil?
|
284
|
+
search_type = if m[1].nil?
|
285
|
+
SL::GoogleSearch.test_for_key ? 'gg' : 'g'
|
286
|
+
else
|
287
|
+
m[1]
|
288
|
+
end
|
283
289
|
|
284
290
|
search_terms = m[2].gsub(/(^["']|["']$)/, '')
|
285
291
|
search_terms.strip!
|
@@ -301,7 +307,7 @@ module SL
|
|
301
307
|
# if the end of input contains "!!", only print the url
|
302
308
|
link_only = true if search_terms =~ /!!\^?$/
|
303
309
|
|
304
|
-
search_terms.sub!(/(!!)?\^?(!!)?$/,
|
310
|
+
search_terms.sub!(/(!!)?\^?(!!)?$/, '')
|
305
311
|
|
306
312
|
elsif link_info =~ /^!/
|
307
313
|
search_word = link_info.match(/^!(\S+)/)
|
@@ -389,7 +395,7 @@ module SL
|
|
389
395
|
|
390
396
|
if (search_type && search_terms) || url
|
391
397
|
# warn "Searching #{search_type} for #{search_terms}"
|
392
|
-
|
398
|
+
unless url
|
393
399
|
search_count += 1
|
394
400
|
url, title, link_text = do_search(search_type, search_terms, link_text, search_count)
|
395
401
|
end
|
@@ -433,7 +439,7 @@ module SL
|
|
433
439
|
res
|
434
440
|
end
|
435
441
|
else
|
436
|
-
|
442
|
+
SL.add_error('No results', "#{search_terms} (#{match_string})")
|
437
443
|
counter_errors += 1
|
438
444
|
match
|
439
445
|
end
|
@@ -564,7 +570,7 @@ module SL
|
|
564
570
|
end
|
565
571
|
terms_p = terms.split(/ +/)
|
566
572
|
if terms_p.length > highest_token
|
567
|
-
remainder = terms_p[highest_token - 1
|
573
|
+
remainder = terms_p[highest_token - 1..-1].join(' ')
|
568
574
|
terms_p = terms_p[0..highest_token - 2]
|
569
575
|
terms_p.push(remainder)
|
570
576
|
end
|
@@ -619,14 +625,11 @@ module SL
|
|
619
625
|
# Social handle expansion
|
620
626
|
when /^([tfilm])?@(\S+)\s*$/
|
621
627
|
type = Regexp.last_match(1)
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
type = 't'
|
628
|
-
end
|
629
|
-
end
|
628
|
+
type ||= if Regexp.last_match(2) =~ /[a-z0-9_]@[a-z0-9_.]+/i
|
629
|
+
'm'
|
630
|
+
else
|
631
|
+
't'
|
632
|
+
end
|
630
633
|
link_text = input.sub(/^[tfilm]/, '')
|
631
634
|
url, title = SL::SocialSearch.social_handle(type, link_text)
|
632
635
|
link_text = title
|
data/lib/searchlink/plist.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# = plist
|
2
4
|
#
|
3
5
|
# Copyright 2006-2010 Ben Bleything and Patrick May
|
4
6
|
# Distributed under the MIT License
|
5
|
-
module Plist
|
7
|
+
module Plist; end
|
6
8
|
|
7
9
|
# === Load a plist file
|
8
10
|
# This is the main point of the library:
|
9
11
|
#
|
10
12
|
# r = Plist::parse_xml( filename_or_xml )
|
11
13
|
module Plist
|
12
|
-
def
|
14
|
+
def self.parse_xml(filename_or_xml)
|
13
15
|
listener = Listener.new
|
14
16
|
parser = StreamParser.new(filename_or_xml, listener)
|
15
17
|
parser.parse
|
@@ -21,18 +23,18 @@ module Plist
|
|
21
23
|
|
22
24
|
def initialize
|
23
25
|
@result = nil
|
24
|
-
@open =
|
26
|
+
@open = []
|
25
27
|
end
|
26
28
|
|
27
|
-
def tag_start(name,
|
28
|
-
@open.push PTag
|
29
|
+
def tag_start(name, _attributes)
|
30
|
+
@open.push PTag.mappings[name].new
|
29
31
|
end
|
30
32
|
|
31
|
-
def text(
|
33
|
+
def text(contents)
|
32
34
|
@open.last.text = contents if @open.last
|
33
35
|
end
|
34
36
|
|
35
|
-
def tag_end(
|
37
|
+
def tag_end(_name)
|
36
38
|
last = @open.pop
|
37
39
|
if @open.empty?
|
38
40
|
@result = last.to_ruby
|
@@ -43,28 +45,28 @@ module Plist
|
|
43
45
|
end
|
44
46
|
|
45
47
|
class StreamParser
|
46
|
-
def initialize(
|
47
|
-
if plist_data_or_file.respond_to? :read
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
def initialize(plist_data_or_file, listener)
|
49
|
+
@xml = if plist_data_or_file.respond_to? :read
|
50
|
+
plist_data_or_file.read
|
51
|
+
elsif File.exist? plist_data_or_file
|
52
|
+
File.read(plist_data_or_file)
|
53
|
+
else
|
54
|
+
plist_data_or_file
|
55
|
+
end
|
54
56
|
|
55
57
|
@listener = listener
|
56
58
|
end
|
57
59
|
|
58
|
-
TEXT
|
59
|
-
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um
|
60
|
-
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
|
61
|
-
COMMENT_START = /\A<!--/u
|
62
|
-
COMMENT_END = /.*?-->/um
|
60
|
+
TEXT = /([^<]+)/.freeze
|
61
|
+
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um.freeze
|
62
|
+
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um.freeze
|
63
|
+
COMMENT_START = /\A<!--/u.freeze
|
64
|
+
COMMENT_END = /.*?-->/um.freeze
|
63
65
|
|
64
66
|
def parse
|
65
|
-
plist_tags = PTag
|
67
|
+
plist_tags = PTag.mappings.keys.join('|')
|
66
68
|
start_tag = /<(#{plist_tags})([^>]*)>/i
|
67
|
-
end_tag =
|
69
|
+
end_tag = %r{</(#{plist_tags})[^>]*>}i
|
68
70
|
|
69
71
|
require 'strscan'
|
70
72
|
|
@@ -76,53 +78,51 @@ module Plist
|
|
76
78
|
elsif @scanner.scan(DOCTYPE_PATTERN)
|
77
79
|
elsif @scanner.scan(start_tag)
|
78
80
|
@listener.tag_start(@scanner[1], nil)
|
79
|
-
if
|
80
|
-
@listener.tag_end(@scanner[1])
|
81
|
-
end
|
81
|
+
@listener.tag_end(@scanner[1]) if @scanner[2] =~ %r{/$}
|
82
82
|
elsif @scanner.scan(TEXT)
|
83
83
|
@listener.text(@scanner[1])
|
84
84
|
elsif @scanner.scan(end_tag)
|
85
85
|
@listener.tag_end(@scanner[1])
|
86
86
|
else
|
87
|
-
raise
|
87
|
+
raise 'Unimplemented element'
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
93
|
class PTag
|
94
|
-
@@mappings = {
|
95
|
-
def
|
94
|
+
@@mappings = {}
|
95
|
+
def self.mappings
|
96
96
|
@@mappings
|
97
97
|
end
|
98
98
|
|
99
|
-
def
|
99
|
+
def self.inherited(sub_class)
|
100
100
|
key = sub_class.to_s.downcase
|
101
|
-
key.gsub!(/^plist::/, ''
|
102
|
-
key.gsub!(/^p/, '')
|
101
|
+
key.gsub!(/^plist::/, '')
|
102
|
+
key.gsub!(/^p/, '') unless key == 'plist'
|
103
103
|
|
104
104
|
@@mappings[key] = sub_class
|
105
105
|
end
|
106
106
|
|
107
107
|
attr_accessor :text, :children
|
108
108
|
def initialize
|
109
|
-
@children =
|
109
|
+
@children = []
|
110
110
|
end
|
111
111
|
|
112
112
|
def to_ruby
|
113
|
-
raise
|
113
|
+
raise 'Unimplemented: ' + self.class.to_s + "#to_ruby on #{inspect}"
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
117
|
class PList < PTag
|
118
118
|
def to_ruby
|
119
|
-
children.first
|
119
|
+
children.first&.to_ruby
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
123
|
class PDict < PTag
|
124
124
|
def to_ruby
|
125
|
-
dict =
|
125
|
+
dict = {}
|
126
126
|
key = nil
|
127
127
|
|
128
128
|
children.each do |c|
|
@@ -140,21 +140,19 @@ module Plist
|
|
140
140
|
|
141
141
|
class PKey < PTag
|
142
142
|
def to_ruby
|
143
|
-
CGI
|
143
|
+
CGI.unescapeHTML(text || '')
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
147
|
class PString < PTag
|
148
148
|
def to_ruby
|
149
|
-
CGI
|
149
|
+
CGI.unescapeHTML(text || '')
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
153
|
class PArray < PTag
|
154
154
|
def to_ruby
|
155
|
-
children.collect
|
156
|
-
c.to_ruby
|
157
|
-
end
|
155
|
+
children.collect(&:to_ruby)
|
158
156
|
end
|
159
157
|
end
|
160
158
|
|
@@ -195,19 +193,17 @@ module Plist
|
|
195
193
|
data = Base64.decode64(text.gsub(/\s+/, ''))
|
196
194
|
|
197
195
|
begin
|
198
|
-
|
196
|
+
Marshal.load(data)
|
199
197
|
rescue Exception
|
200
198
|
io = StringIO.new
|
201
199
|
io.write data
|
202
200
|
io.rewind
|
203
|
-
|
201
|
+
io
|
204
202
|
end
|
205
203
|
end
|
206
204
|
end
|
207
205
|
end
|
208
206
|
|
209
|
-
|
210
207
|
# module Plist
|
211
208
|
# VERSION = '3.1.0'
|
212
209
|
# end
|
213
|
-
|
data/lib/searchlink/search.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
# DuckDuckGo Search
|
3
5
|
class DuckDuckGoSearch
|
@@ -36,36 +38,34 @@ module SL
|
|
36
38
|
|
37
39
|
# return SL.ddg(search_terms, link_text) if search_type == 'g' && SL::GoogleSearch.test_for_key
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
page = Curl::Html.new("https://duckduckgo.com/?q=#{terms}", compressed: true)
|
41
|
+
terms = "%5C#{search_terms.url_encode}"
|
42
|
+
page = Curl::Html.new("https://duckduckgo.com/?q=#{terms}", compressed: true)
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
locs = page.meta['refresh'].match(%r{/l/\?uddg=(.*?)$})
|
45
|
+
locs = page.body.match(%r{/l/\?uddg=(.*?)'}) if locs.nil?
|
46
|
+
locs = page.body.match(/url=(.*?)'/) if locs.nil?
|
46
47
|
|
47
|
-
|
48
|
+
return false if locs.nil?
|
48
49
|
|
49
|
-
|
50
|
+
url = locs[1].url_decode.sub(/&rut=\w+/, '')
|
50
51
|
|
51
|
-
|
52
|
-
|
52
|
+
result = url.strip.url_decode || false
|
53
|
+
return false unless result
|
53
54
|
|
54
|
-
|
55
|
+
return false if result =~ /internal-search\.duckduckgo\.com/
|
55
56
|
|
56
|
-
|
57
|
-
|
57
|
+
# output_url = CGI.unescape(result)
|
58
|
+
output_url = result
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
output_title = if SL.config['include_titles'] || SL.titleize
|
61
|
+
SL::URL.title(output_url) || ''
|
62
|
+
else
|
63
|
+
''
|
64
|
+
end
|
64
65
|
|
65
|
-
|
66
|
+
output_url = SL.first_image(output_url) if search_type =~ /img$/
|
66
67
|
|
67
|
-
|
68
|
-
end
|
68
|
+
[output_url, output_title, link_text]
|
69
69
|
end
|
70
70
|
|
71
71
|
# Searches DuckDuckGo for the given search terms and
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
# GitHub search
|
3
5
|
class GitHubSearch
|
@@ -59,6 +61,11 @@ module SL
|
|
59
61
|
|
60
62
|
res = Curl::Json.new(url, headers: headers).json
|
61
63
|
|
64
|
+
if res.is_a?(Hash) && res['status'].to_i == 401
|
65
|
+
SL.notify('Error', 'Bad GitHub credentials')
|
66
|
+
return nil
|
67
|
+
end
|
68
|
+
|
62
69
|
best = nil
|
63
70
|
best = filter_gists(res, search_terms) if res
|
64
71
|
|
@@ -209,7 +216,7 @@ module SL
|
|
209
216
|
else
|
210
217
|
if terms.split(/ +/).count > 1
|
211
218
|
parts = terms.split(/ +/)
|
212
|
-
gist = search_user_gists(parts[0], parts[1
|
219
|
+
gist = search_user_gists(parts[0], parts[1..-1].join(' '))
|
213
220
|
|
214
221
|
if gist
|
215
222
|
url = gist[:url]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
##
|
2
4
|
## Chromium (Chrome, Arc, Brave, Edge) search methods
|
3
5
|
##
|
@@ -273,7 +275,7 @@ module SL
|
|
273
275
|
if json.key? 'children'
|
274
276
|
urls = extract_chrome_bookmarks(json['children'], urls, term)
|
275
277
|
elsif json['type'] == 'url'
|
276
|
-
date = Time.at(json['date_added'].to_i /
|
278
|
+
date = Time.at(json['date_added'].to_i / 1_000_000 + Time.new(1601, 0o1, 0o1).strftime('%s').to_i)
|
277
279
|
url = { url: json['url'], title: json['name'], date: date }
|
278
280
|
score = score_mark(url, term)
|
279
281
|
|
@@ -299,7 +301,7 @@ module SL
|
|
299
301
|
def score_mark(mark, terms)
|
300
302
|
return 0 unless mark[:url]
|
301
303
|
|
302
|
-
score = if mark[:title]
|
304
|
+
score = if mark[:title]&.matches_exact(terms)
|
303
305
|
12 + mark[:url].matches_score(terms, start_word: false)
|
304
306
|
elsif mark[:url].matches_exact(terms)
|
305
307
|
11
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
class HistorySearch
|
3
5
|
class << self
|
@@ -56,12 +58,11 @@ module SL
|
|
56
58
|
|
57
59
|
marks.map! do |bm|
|
58
60
|
date = Time.parse(bm['datum'])
|
59
|
-
score = score_mark({url: bm['url'], title: bm['title']}, term)
|
61
|
+
score = score_mark({ url: bm['url'], title: bm['title'] }, term)
|
60
62
|
{ url: bm['url'], title: bm['title'], date: date, score: score }
|
61
63
|
end
|
62
64
|
|
63
|
-
|
64
|
-
m = marks.sort_by { |m| [m[:url].length * -1, m[:score]] }.last
|
65
|
+
m = marks.max_by { |m| [m[:url].length * -1, m[:score]] }
|
65
66
|
|
66
67
|
[m[:url], m[:title], m[:date]]
|
67
68
|
else
|
@@ -124,7 +125,7 @@ module SL
|
|
124
125
|
bm = JSON.parse(most_recent)[0]
|
125
126
|
|
126
127
|
date = Time.parse(bm['datum'])
|
127
|
-
score = score_mark({url: bm['url'], title: bm['title']}, term)
|
128
|
+
score = score_mark({ url: bm['url'], title: bm['title'] }, term)
|
128
129
|
[bm['url'], bm['title'], date, score]
|
129
130
|
else
|
130
131
|
false
|
@@ -1,10 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# import
|
2
4
|
require_relative 'helpers/chromium'
|
3
5
|
|
4
|
-
#import
|
6
|
+
# import
|
5
7
|
require_relative 'helpers/firefox'
|
6
8
|
|
7
|
-
#import
|
9
|
+
# import
|
8
10
|
require_relative 'helpers/safari'
|
9
11
|
|
10
12
|
module SL
|
@@ -48,7 +50,7 @@ module SL
|
|
48
50
|
str = search_type.match(/^h(([scfabe])([hb])?)*$/)[1]
|
49
51
|
|
50
52
|
types = []
|
51
|
-
while str
|
53
|
+
while str&.length&.positive?
|
52
54
|
if str =~ /^s([hb]*)/
|
53
55
|
t = Regexp.last_match(1)
|
54
56
|
if t.length > 1 || t.empty?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
# Spotlight file search
|
3
5
|
class SpotlightSearch
|
@@ -14,7 +16,7 @@ module SL
|
|
14
16
|
def search(_, search_terms, link_text)
|
15
17
|
query = search_terms.gsub(/%22/, '"')
|
16
18
|
matches = `mdfind '#{query}' 2>/dev/null`.strip.split(/\n/)
|
17
|
-
res = matches.
|
19
|
+
res = matches.min_by { |r| File.basename(r).length }
|
18
20
|
return [false, query, link_text] if res.strip.empty?
|
19
21
|
|
20
22
|
title = File.basename(res)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
class TwitterSearch
|
3
5
|
class << self
|
@@ -10,7 +12,7 @@ module SL
|
|
10
12
|
}
|
11
13
|
end
|
12
14
|
|
13
|
-
def search(
|
15
|
+
def search(_search_type, search_terms, link_text)
|
14
16
|
if SL::URL.url?(search_terms) && search_terms =~ %r{^https://twitter.com/}
|
15
17
|
url, title = twitter_embed(search_terms)
|
16
18
|
else
|
@@ -37,7 +39,7 @@ module SL
|
|
37
39
|
else
|
38
40
|
return [false, 'Error retrieving tweet']
|
39
41
|
end
|
40
|
-
|
42
|
+
[url, title]
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
data/lib/searchlink/searches.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
module Searches
|
3
5
|
class << self
|
@@ -14,7 +16,7 @@ module SL
|
|
14
16
|
#
|
15
17
|
# @param [String, Array] title title or array of titles
|
16
18
|
# @param [Symbol] type plugin type (:search)
|
17
|
-
# @param [Class] klass class that handles plugin actions. Search plugins
|
19
|
+
# @param [Class] klass class that handles plugin actions. Search plugins
|
18
20
|
# must have a #settings and a #search method
|
19
21
|
#
|
20
22
|
def register(title, type, klass)
|
@@ -48,7 +50,7 @@ module SL
|
|
48
50
|
'<tbody>']
|
49
51
|
|
50
52
|
searches.each do |s|
|
51
|
-
out << "<tr><td><code>!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1
|
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>"
|
52
54
|
end
|
53
55
|
out.concat(['</tbody>', '</table>']).join("\n")
|
54
56
|
end
|
@@ -63,7 +65,7 @@ module SL
|
|
63
65
|
plugins[:search].each { |_, plugin| searches.concat(plugin[:searches].delete_if { |s| s[1].nil? }) }
|
64
66
|
out = []
|
65
67
|
searches.each do |s|
|
66
|
-
out += "!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1
|
68
|
+
out += "!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1..-1].join(',')})" : s[0]}#{s[0].spacer}#{s[1]}"
|
67
69
|
end
|
68
70
|
out.join("\n")
|
69
71
|
end
|
data/lib/searchlink/semver.rb
CHANGED
data/lib/searchlink/string.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
4
|
# String helpers
|
3
5
|
class ::String
|
@@ -275,7 +277,7 @@ module SL
|
|
275
277
|
|
276
278
|
next if parts.length == 1
|
277
279
|
|
278
|
-
remaining_separators = seo_title_separators[i
|
280
|
+
remaining_separators = seo_title_separators[i..-1].map { |s| Regexp.escape(s) }.join('')
|
279
281
|
seps = Regexp.new("^[^#{remaining_separators}]+$")
|
280
282
|
|
281
283
|
longest = parts.longest_element.strip
|
@@ -396,24 +398,25 @@ module SL
|
|
396
398
|
end
|
397
399
|
|
398
400
|
def distance(t)
|
399
|
-
s =
|
401
|
+
s = dup
|
400
402
|
m = s.length
|
401
403
|
n = t.length
|
402
|
-
return m if n
|
403
|
-
return n if m
|
404
|
-
|
404
|
+
return m if n.zero?
|
405
|
+
return n if m.zero?
|
406
|
+
|
407
|
+
d = Array.new(m + 1) { Array.new(n + 1) }
|
405
408
|
|
406
|
-
(0..m).each {|i| d[i][0] = i}
|
407
|
-
(0..n).each {|j| d[0][j] = j}
|
409
|
+
(0..m).each { |i| d[i][0] = i }
|
410
|
+
(0..n).each { |j| d[0][j] = j }
|
408
411
|
(1..n).each do |j|
|
409
412
|
(1..m).each do |i|
|
410
|
-
d[i][j] = if s[i-1] == t[j-1]
|
411
|
-
d[i-1][j-1] # no operation required
|
413
|
+
d[i][j] = if s[i - 1] == t[j - 1] # adjust index into string
|
414
|
+
d[i - 1][j - 1] # no operation required
|
412
415
|
else
|
413
|
-
[
|
414
|
-
|
415
|
-
|
416
|
-
|
416
|
+
[d[i - 1][j] + 1, # deletion
|
417
|
+
d[i][j - 1] + 1, # insertion
|
418
|
+
d[i - 1][j - 1] + 1 # substitution
|
419
|
+
].min
|
417
420
|
end
|
418
421
|
end
|
419
422
|
end
|
data/lib/searchlink/url.rb
CHANGED
data/lib/searchlink/util.rb
CHANGED
data/lib/searchlink/version.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SL
|
2
|
-
VERSION = '2.3.
|
4
|
+
VERSION = '2.3.68'
|
3
5
|
end
|
4
6
|
|
7
|
+
# Main module
|
5
8
|
module SL
|
6
9
|
class << self
|
7
10
|
def version_check
|
@@ -68,7 +71,7 @@ module SL
|
|
68
71
|
end
|
69
72
|
|
70
73
|
def update_searchlink
|
71
|
-
if
|
74
|
+
if `uname`.strip !~ /Darwin/
|
72
75
|
add_output('Auto updating only available on macOS')
|
73
76
|
return
|
74
77
|
end
|
data/lib/searchlink/which.rb
CHANGED
@@ -5,171 +5,166 @@
|
|
5
5
|
|
6
6
|
module TTY
|
7
7
|
module Which
|
8
|
-
VERSION =
|
9
|
-
end
|
10
|
-
end
|
8
|
+
VERSION = '0.5.0'
|
9
|
+
end
|
10
|
+
end
|
11
11
|
|
12
12
|
module TTY
|
13
13
|
# A class responsible for finding an executable in the PATH
|
14
14
|
module Which
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
15
|
+
class << self
|
16
|
+
# Find an executable in a platform independent way
|
17
|
+
#
|
18
|
+
# @param [String] cmd
|
19
|
+
# the command to search for
|
20
|
+
# @param [Array<String>] paths
|
21
|
+
# the paths to look through
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# which("ruby") # => "/usr/local/bin/ruby"
|
25
|
+
# which("/usr/local/bin/ruby") # => "/usr/local/bin/ruby"
|
26
|
+
# which("foo") # => nil
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# which("ruby", paths: ["/usr/locale/bin", "/usr/bin", "/bin"])
|
30
|
+
#
|
31
|
+
# @return [String, nil]
|
32
|
+
# the absolute path to executable if found, `nil` otherwise
|
33
|
+
#
|
34
|
+
# @api public
|
35
|
+
def which(cmd, paths: search_paths)
|
36
|
+
if file_with_path?(cmd)
|
37
|
+
return cmd if executable_file?(cmd)
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
extensions.each do |ext|
|
40
|
+
exe = "#{cmd}#{ext}"
|
41
|
+
return ::File.absolute_path(exe) if executable_file?(exe)
|
42
|
+
end
|
43
|
+
return nil
|
41
44
|
end
|
42
|
-
return nil
|
43
|
-
end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
46
|
+
paths.each do |path|
|
47
|
+
if file_with_exec_ext?(cmd)
|
48
|
+
exe = ::File.join(path, cmd)
|
49
|
+
return ::File.absolute_path(exe) if executable_file?(exe)
|
50
|
+
end
|
51
|
+
extensions.each do |ext|
|
52
|
+
exe = ::File.join(path, "#{cmd}#{ext}")
|
53
|
+
return ::File.absolute_path(exe) if executable_file?(exe)
|
54
|
+
end
|
53
55
|
end
|
56
|
+
nil
|
54
57
|
end
|
55
|
-
nil
|
56
|
-
end
|
57
|
-
module_function :which
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
module_function :exist?
|
59
|
+
# Check if executable exists in the path
|
60
|
+
#
|
61
|
+
# @param [String] cmd
|
62
|
+
# the executable to check
|
63
|
+
#
|
64
|
+
# @param [Array<String>] paths
|
65
|
+
# paths to check
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
def exist?(cmd, paths: search_paths)
|
71
|
+
!which(cmd, paths: paths).nil?
|
72
|
+
end
|
74
73
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
module_function :search_paths
|
74
|
+
# Find default system paths
|
75
|
+
#
|
76
|
+
# @param [String] path
|
77
|
+
# the path to search through
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# search_paths("/usr/local/bin:/bin")
|
81
|
+
# # => ["/bin"]
|
82
|
+
#
|
83
|
+
# @return [Array<String>]
|
84
|
+
# the array of paths to search
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
def search_paths(path = ENV['PATH'])
|
88
|
+
paths = if path && !path.empty?
|
89
|
+
path.split(::File::PATH_SEPARATOR)
|
90
|
+
else
|
91
|
+
%w[/usr/local/bin /usr/ucb /usr/bin /bin /opt/homebrew/bin]
|
92
|
+
end
|
93
|
+
paths.select(&Dir.method(:exist?))
|
94
|
+
end
|
97
95
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
96
|
+
# All possible file extensions
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# extensions(".exe;cmd;.bat")
|
100
|
+
# # => [".exe", ".bat"]
|
101
|
+
#
|
102
|
+
# @param [String] path_ext
|
103
|
+
# a string of semicolon separated filename extensions
|
104
|
+
#
|
105
|
+
# @return [Array<String>]
|
106
|
+
# an array with valid file extensions
|
107
|
+
#
|
108
|
+
# @api private
|
109
|
+
def extensions(path_ext = ENV['PATHEXT'])
|
110
|
+
return [''] unless path_ext
|
113
111
|
|
114
|
-
|
115
|
-
|
116
|
-
module_function :extensions
|
112
|
+
path_ext.split(::File::PATH_SEPARATOR).select { |part| part.include?('.') }
|
113
|
+
end
|
117
114
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
module_function :executable_file?
|
115
|
+
# Determines if filename is an executable file
|
116
|
+
#
|
117
|
+
# @example Basic usage
|
118
|
+
# executable_file?("/usr/bin/less") # => true
|
119
|
+
#
|
120
|
+
# @example Executable in directory
|
121
|
+
# executable_file?("less", "/usr/bin") # => true
|
122
|
+
# executable_file?("less", "/usr") # => false
|
123
|
+
#
|
124
|
+
# @param [String] filename
|
125
|
+
# the path to file
|
126
|
+
# @param [String] dir
|
127
|
+
# the directory within which to search for filename
|
128
|
+
#
|
129
|
+
# @return [Boolean]
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def executable_file?(filename, dir = nil)
|
133
|
+
path = ::File.join(dir, filename) if dir
|
134
|
+
path ||= filename
|
135
|
+
::File.file?(path) && ::File.executable?(path)
|
136
|
+
end
|
141
137
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
138
|
+
# Check if command itself has executable extension
|
139
|
+
#
|
140
|
+
# @param [String] filename
|
141
|
+
# the path to executable file
|
142
|
+
#
|
143
|
+
# @example
|
144
|
+
# file_with_exec_ext?("file.bat")
|
145
|
+
# # => true
|
146
|
+
#
|
147
|
+
# @return [Boolean]
|
148
|
+
#
|
149
|
+
# @api private
|
150
|
+
def file_with_exec_ext?(filename)
|
151
|
+
extension = ::File.extname(filename)
|
152
|
+
return false if extension.empty?
|
157
153
|
|
158
|
-
|
159
|
-
|
160
|
-
module_function :file_with_exec_ext?
|
154
|
+
extensions.any? { |ext| extension.casecmp(ext).zero? }
|
155
|
+
end
|
161
156
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
157
|
+
# Check if executable file is part of absolute/relative path
|
158
|
+
#
|
159
|
+
# @param [String] cmd
|
160
|
+
# the executable to check
|
161
|
+
#
|
162
|
+
# @return [Boolean]
|
163
|
+
#
|
164
|
+
# @api private
|
165
|
+
def file_with_path?(cmd)
|
166
|
+
::File.expand_path(cmd) == cmd
|
167
|
+
end
|
172
168
|
end
|
173
|
-
|
174
|
-
|
175
|
-
end # TTY
|
169
|
+
end
|
170
|
+
end
|
data/lib/searchlink.rb
CHANGED
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.
|
4
|
+
version: 2.3.68
|
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-
|
11
|
+
date: 2024-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -292,7 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
292
292
|
- !ruby/object:Gem::Version
|
293
293
|
version: '0'
|
294
294
|
requirements: []
|
295
|
-
rubygems_version: 3.2.
|
295
|
+
rubygems_version: 3.2.15
|
296
296
|
signing_key:
|
297
297
|
specification_version: 4
|
298
298
|
summary: Create Markdown links from web searches without leaving your editor.
|