searchlink 2.3.84 → 2.3.85
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.
- checksums.yaml +4 -4
- data/lib/searchlink/output.rb +33 -2
- data/lib/searchlink/parse.rb +219 -255
- data/lib/searchlink/searches/bitly.rb +0 -1
- data/lib/searchlink/searches/helpers/safari.rb +6 -1
- data/lib/searchlink/searches/history.rb +5 -6
- data/lib/searchlink/searches/tinyurl.rb +72 -0
- data/lib/searchlink/searches.rb +3 -0
- data/lib/searchlink/string.rb +67 -0
- data/lib/searchlink/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02b111da9ce13f8646d7357ae5560a1c6b84bb45fd506dca8fafe96e1c844bea
|
4
|
+
data.tar.gz: 02fd69545df789fdf1105dea4971b53f02bfc33ac0da20a3f25454c11f9a9ae7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7af24cad60ca01ead8ab4a76f82c6d6fa9be6aa30752f088c23cfe387d9a3adf8f076e5e4d1f1dc25d9eeda8879d1ae7e83bbee3bcea250de6914dc67d35cf0c
|
7
|
+
data.tar.gz: '089fb329c4c7be4cf30bf929b8c4309374a0b8055f42eb2adeb2c62d29f7a9ef84d9e45527a5c0a13c7e0061345ee814cfd431bc6f33614ad034f8d8c970250a'
|
data/lib/searchlink/output.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
module SL
|
4
4
|
class << self
|
5
5
|
attr_writer :titleize, :clipboard, :output, :footer, :line_num,
|
6
|
-
:match_column, :match_length, :originput, :errors, :report, :printout
|
6
|
+
:match_column, :match_length, :originput, :errors, :report, :printout,
|
7
|
+
:shortener
|
7
8
|
|
8
9
|
# Whether or not to add a title to the output
|
9
10
|
def titleize
|
@@ -60,6 +61,16 @@ module SL
|
|
60
61
|
@errors ||= {}
|
61
62
|
end
|
62
63
|
|
64
|
+
# Stores query parameters
|
65
|
+
def query
|
66
|
+
@query ||= {}
|
67
|
+
end
|
68
|
+
|
69
|
+
# The shortener to use
|
70
|
+
def shortener
|
71
|
+
@shortener ||= :none
|
72
|
+
end
|
73
|
+
|
63
74
|
# Posts macOS notifications
|
64
75
|
#
|
65
76
|
# @param title [String] The title of the notification
|
@@ -102,6 +113,19 @@ module SL
|
|
102
113
|
|
103
114
|
title = title.gsub(/[ \t]+/, " ")
|
104
115
|
|
116
|
+
url.add_query_string!
|
117
|
+
|
118
|
+
url = case SL.shortener
|
119
|
+
when :isgd
|
120
|
+
SL::IsgdSearch.shorten(url)
|
121
|
+
when :tinyurl
|
122
|
+
SL::TinyurlSearch.shorten(url)
|
123
|
+
when :bitly
|
124
|
+
SL::BitlySearch.shorten(url)
|
125
|
+
else
|
126
|
+
url
|
127
|
+
end
|
128
|
+
|
105
129
|
case type.to_sym
|
106
130
|
when :ref_title
|
107
131
|
%(\n[#{text}]: #{url}#{title})
|
@@ -141,7 +165,6 @@ module SL
|
|
141
165
|
#
|
142
166
|
def print_footer
|
143
167
|
unless SL.footer.empty?
|
144
|
-
|
145
168
|
footnotes = []
|
146
169
|
SL.footer.delete_if do |note|
|
147
170
|
note.strip!
|
@@ -202,6 +225,14 @@ module SL
|
|
202
225
|
SL.errors[type].push("(#{position}): #{str}")
|
203
226
|
end
|
204
227
|
|
228
|
+
# Add to query string
|
229
|
+
# @param hsh [Hash] The queries to add
|
230
|
+
# @return [nil]
|
231
|
+
def add_query(hsh)
|
232
|
+
SL.query ||= {}
|
233
|
+
SL.query.merge!(hsh)
|
234
|
+
end
|
235
|
+
|
205
236
|
# Prints the report.
|
206
237
|
#
|
207
238
|
# @return [String] The report.
|
data/lib/searchlink/parse.rb
CHANGED
@@ -2,194 +2,6 @@
|
|
2
2
|
|
3
3
|
module SL
|
4
4
|
class SearchLink
|
5
|
-
# Parse arguments in the input string
|
6
|
-
#
|
7
|
-
# @param string [String] the string to parse
|
8
|
-
# @param opt [Hash] the options to parse
|
9
|
-
# @option opt [Boolean] :only_meta (false) whether to skip flags
|
10
|
-
# @option opt [Boolean] :no_restore (false) whether to restore previous config
|
11
|
-
# @return [String] the parsed string
|
12
|
-
#
|
13
|
-
def parse_arguments(string, opt = {})
|
14
|
-
input = string.dup
|
15
|
-
return "" if input.nil?
|
16
|
-
|
17
|
-
skip_flags = opt[:only_meta] || false
|
18
|
-
no_restore = opt[:no_restore] || false
|
19
|
-
restore_prev_config unless no_restore
|
20
|
-
|
21
|
-
input.parse_flags! unless skip_flags
|
22
|
-
|
23
|
-
options = %w[debug country_code inline prefix_random include_titles remove_seo validate_links complete_bare]
|
24
|
-
options.each do |o|
|
25
|
-
if input =~ /^ *#{o}:\s+(\S+)$/
|
26
|
-
val = Regexp.last_match(1).strip
|
27
|
-
|
28
|
-
if val.is_a?(String)
|
29
|
-
value = true if val =~ /true/i
|
30
|
-
value = false if val =~ /false/i
|
31
|
-
end
|
32
|
-
val = value if value
|
33
|
-
SL.config[o] = val
|
34
|
-
warn "\r\033[0KGlobal config: #{o} = #{SL.config[o]}\n" unless SILENT
|
35
|
-
end
|
36
|
-
|
37
|
-
next if skip_flags
|
38
|
-
|
39
|
-
while input =~ /^#{o}:\s+(.*?)$/ || input =~ /--(no-)?#{o}/
|
40
|
-
next unless input =~ /--(no-)?#{o}/ && !skip_flags
|
41
|
-
|
42
|
-
unless SL.prev_config.key? o
|
43
|
-
SL.prev_config[o] = SL.config[o]
|
44
|
-
bool = Regexp.last_match(1).nil? || Regexp.last_match(1) == "" ? true : false
|
45
|
-
SL.config[o] = bool
|
46
|
-
$stderr.print "\r\033[0KLine config: #{o} = #{SL.config[o]}\n" unless SILENT
|
47
|
-
end
|
48
|
-
input.sub!(/\s?--(no-)?#{o}/, "")
|
49
|
-
end
|
50
|
-
end
|
51
|
-
SL.clipboard ? string : input
|
52
|
-
end
|
53
|
-
|
54
|
-
# Parse commands from the given input string
|
55
|
-
#
|
56
|
-
# @param input [String] the input string
|
57
|
-
def parse_commands(input)
|
58
|
-
# Handle commands like help or docs
|
59
|
-
return unless input.strip =~ /^!?(h(elp)?|wiki|docs?|v(er(s(ion)?)?)?|up(date|grade))$/
|
60
|
-
|
61
|
-
case input.strip
|
62
|
-
when /^!?help$/i
|
63
|
-
if SILENT
|
64
|
-
help_dialog
|
65
|
-
else
|
66
|
-
$stdout.puts SL.version_check.to_s
|
67
|
-
$stdout.puts "See https://github.com/ttscoff/searchlink/wiki for help"
|
68
|
-
end
|
69
|
-
print input
|
70
|
-
when /^!?(wiki|docs)$/i
|
71
|
-
warn "Opening wiki in browser"
|
72
|
-
`open https://github.com/ttscoff/searchlink/wiki`
|
73
|
-
when /^!?v(er(s(ion)?)?)?$/
|
74
|
-
print "[#{SL.version_check}]"
|
75
|
-
when /^!?up(date|grade)$/
|
76
|
-
SL.update_searchlink
|
77
|
-
print SL.output.join("")
|
78
|
-
end
|
79
|
-
Process.exit 0
|
80
|
-
end
|
81
|
-
|
82
|
-
def create_footnote(mtch)
|
83
|
-
if mtch[1].nil? || mtch[1] == ""
|
84
|
-
match
|
85
|
-
else
|
86
|
-
note = mtch[1].strip
|
87
|
-
@footnote_counter += 1
|
88
|
-
ref = if !@link_text.empty? && @link_text.scan(/\s/).empty?
|
89
|
-
@link_text
|
90
|
-
else
|
91
|
-
format("%<p>sfn%<c>04d", p: @prefix, c: @footnote_counter)
|
92
|
-
end
|
93
|
-
SL.add_footer "[^#{ref}]: #{note}"
|
94
|
-
res = "[^#{ref}]"
|
95
|
-
@cursor_difference += (SL.match_length - res.length)
|
96
|
-
SL.match_length = res.length
|
97
|
-
SL.add_report("#{@match_string} => Footnote #{ref}")
|
98
|
-
res
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def add_title(link_info)
|
103
|
-
@url = link_info
|
104
|
-
title = SL::URL.title(@url)
|
105
|
-
@link_text = title
|
106
|
-
|
107
|
-
if @ref_title
|
108
|
-
unless @links.key? @url
|
109
|
-
@links[@url] = @link_text
|
110
|
-
SL.add_footer SL.make_link(:ref_title, @link_text, @url, title: title, force_title: false)
|
111
|
-
end
|
112
|
-
@delete_line = true
|
113
|
-
elsif SL.config["inline"]
|
114
|
-
res = SL.make_link(:inline, @link_text, @url, title: title, force_title: false)
|
115
|
-
@cursor_difference += SL.match_length - res.length
|
116
|
-
SL.match_length = res.length
|
117
|
-
SL.add_report("#{@match_string} => #{@url}")
|
118
|
-
res
|
119
|
-
else
|
120
|
-
unless @links.key? @url
|
121
|
-
@highest_marker += 1
|
122
|
-
@links[@url] = format("%<pre>s%<m>04d", pre: @prefix, m: @highest_marker)
|
123
|
-
SL.add_footer SL.make_link(:ref_title, @links[@url], @url, title: title, force_title: false)
|
124
|
-
end
|
125
|
-
|
126
|
-
type = SL.config["inline"] ? :inline : :ref_link
|
127
|
-
res = SL.make_link(type, @link_text, @links[@url], title: false, force_title: false)
|
128
|
-
@cursor_difference += SL.match_length - res.length
|
129
|
-
SL.match_length = res.length
|
130
|
-
SL.add_report("#{@match_string} => #{@url}")
|
131
|
-
res
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def custom_search(search_type, search_terms)
|
136
|
-
SL.config["custom_site_searches"].each do |k, v|
|
137
|
-
next unless search_type == k
|
138
|
-
|
139
|
-
@link_text = search_terms if !SL.titleize && @link_text == ""
|
140
|
-
v = parse_arguments(v, { no_restore: true })
|
141
|
-
if v =~ %r{^(/|http)}i
|
142
|
-
search_type = "r"
|
143
|
-
tokens = v.scan(/\$term\d+[ds]?/).sort.uniq
|
144
|
-
|
145
|
-
if !tokens.empty?
|
146
|
-
highest_token = 0
|
147
|
-
tokens.each do |token|
|
148
|
-
if token =~ /(\d+)[ds]?$/ && Regexp.last_match(1).to_i > highest_token
|
149
|
-
highest_token = Regexp.last_match(1).to_i
|
150
|
-
end
|
151
|
-
end
|
152
|
-
terms_p = search_terms.split(/ +/)
|
153
|
-
if terms_p.length > highest_token
|
154
|
-
remainder = terms_p[highest_token - 1..].join(" ")
|
155
|
-
terms_p = terms_p[0..highest_token - 2]
|
156
|
-
terms_p.push(remainder)
|
157
|
-
end
|
158
|
-
tokens.each do |t|
|
159
|
-
next unless t =~ /(\d+)[ds]?$/
|
160
|
-
|
161
|
-
int = Regexp.last_match(1).to_i - 1
|
162
|
-
replacement = terms_p[int]
|
163
|
-
case t
|
164
|
-
when /d$/
|
165
|
-
replacement.downcase!
|
166
|
-
re_down = ""
|
167
|
-
when /s$/
|
168
|
-
replacement.slugify!
|
169
|
-
re_down = ""
|
170
|
-
else
|
171
|
-
re_down = "(?!d|s)"
|
172
|
-
end
|
173
|
-
v.gsub!(/#{Regexp.escape(t) + re_down}/, replacement.url_encode)
|
174
|
-
end
|
175
|
-
search_terms = v
|
176
|
-
else
|
177
|
-
search_terms = v.gsub(/\$term[ds]?/i) do |mtch|
|
178
|
-
search_terms.downcase! if mtch =~ /d$/i
|
179
|
-
search_terms.slugify! if mtch =~ /s$/i
|
180
|
-
search_terms.url_encode
|
181
|
-
end
|
182
|
-
end
|
183
|
-
else
|
184
|
-
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
185
|
-
search_terms = "site:#{v} #{search_terms}"
|
186
|
-
end
|
187
|
-
|
188
|
-
break
|
189
|
-
end
|
190
|
-
[search_type, search_terms]
|
191
|
-
end
|
192
|
-
|
193
5
|
def parse(input)
|
194
6
|
SL.output = []
|
195
7
|
return false if input.empty?
|
@@ -202,6 +14,7 @@ module SL
|
|
202
14
|
SL.config["inline"] = true if input.scan(/\]\(/).length == 1 && input.split(/\n/).length == 1
|
203
15
|
SL.errors = {}
|
204
16
|
SL.report = []
|
17
|
+
SL.shortener = :none
|
205
18
|
|
206
19
|
# Check for new version
|
207
20
|
latest_version = SL.new_version?
|
@@ -274,6 +87,7 @@ module SL
|
|
274
87
|
end
|
275
88
|
end
|
276
89
|
|
90
|
+
# Handle links in the form of [text](url) or [text](url "title")
|
277
91
|
if input =~ /\[(.*?)\]\((.*?)\)/
|
278
92
|
lines = input.split(/\n/)
|
279
93
|
out = []
|
@@ -330,6 +144,7 @@ module SL
|
|
330
144
|
|
331
145
|
@link_text = this_match[1] || ""
|
332
146
|
link_info = parse_arguments(this_match[2].strip).strip || ""
|
147
|
+
query, link_info = link_info.extract_query({})
|
333
148
|
|
334
149
|
if @link_text.strip == "" && link_info =~ /".*?"/
|
335
150
|
link_info.gsub!(/"(.*?)"/) do
|
@@ -355,7 +170,7 @@ module SL
|
|
355
170
|
end
|
356
171
|
|
357
172
|
if link_info =~ /^!(\S+)/
|
358
|
-
search_type = Regexp.last_match(1)
|
173
|
+
search_type = Regexp.last_match(1).extract_shortener
|
359
174
|
unless SL::Searches.valid_search?(search_type) || search_type =~ /^(\S+\.)+\S+$/
|
360
175
|
SL.add_error("Invalid search#{SL::Searches.did_you_mean(search_type)}", match)
|
361
176
|
invalid_search = true
|
@@ -386,10 +201,12 @@ module SL
|
|
386
201
|
m = Regexp.last_match
|
387
202
|
|
388
203
|
search_type = if m[1].nil?
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
204
|
+
SL::GoogleSearch.api_key? ? "gg" : "g"
|
205
|
+
else
|
206
|
+
m[1]
|
207
|
+
end
|
208
|
+
|
209
|
+
search_type.extract_shortener!
|
393
210
|
|
394
211
|
search_terms = m[2].gsub(/(^["']|["']$)/, "")
|
395
212
|
search_terms.strip!
|
@@ -419,18 +236,17 @@ module SL
|
|
419
236
|
search_type = "g"
|
420
237
|
search_terms = "site:#{m[1]} #{search_terms}"
|
421
238
|
end
|
422
|
-
|
423
239
|
elsif link_info =~ /^!/
|
424
240
|
search_word = link_info.match(/^!(\S+)/)
|
425
|
-
|
426
|
-
if search_word && SL::Searches.valid_search?(
|
427
|
-
search_type =
|
241
|
+
st = search_word[1].extract_shortener
|
242
|
+
if search_word && SL::Searches.valid_search?(st)
|
243
|
+
search_type = st unless search_word.nil?
|
428
244
|
search_terms = @link_text
|
429
|
-
elsif search_word &&
|
245
|
+
elsif search_word && st =~ /^(\S+\.)+\S+$/
|
430
246
|
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
431
247
|
search_terms = "site:#{search_word[1]} #{@link_text}"
|
432
248
|
else
|
433
|
-
SL.add_error("Invalid search#{SL::Searches.did_you_mean(
|
249
|
+
SL.add_error("Invalid search#{SL::Searches.did_you_mean(st)}", match)
|
434
250
|
search_type = false
|
435
251
|
search_terms = false
|
436
252
|
end
|
@@ -450,6 +266,8 @@ module SL
|
|
450
266
|
search_type, search_terms = custom_search(search_type, search_terms)
|
451
267
|
end
|
452
268
|
|
269
|
+
SL.add_query(query) if query
|
270
|
+
|
453
271
|
if (search_type && search_terms) || @url
|
454
272
|
# warn "Searching #{search_type} for #{search_terms}"
|
455
273
|
|
@@ -544,12 +362,13 @@ module SL
|
|
544
362
|
SL.add_report("Processed: #{total_links} links, #{counter_errors} errors.")
|
545
363
|
SL.print_report
|
546
364
|
SL.print_errors
|
547
|
-
else
|
365
|
+
else # Assume single line input
|
548
366
|
link_only = false
|
549
367
|
SL.clipboard = false
|
550
368
|
|
551
369
|
res = parse_arguments(input.strip!).strip
|
552
370
|
input = res.nil? ? input.strip : res
|
371
|
+
query, input = input.extract_query({})
|
553
372
|
|
554
373
|
# if the end of input contain "^", copy to clipboard instead of STDOUT
|
555
374
|
SL.clipboard = true if input =~ /\^[!~:\s]*$/
|
@@ -608,8 +427,9 @@ module SL
|
|
608
427
|
|
609
428
|
case input
|
610
429
|
when /^!(\S+)\s+(.*)$/
|
611
|
-
type = Regexp.last_match(1)
|
430
|
+
type = Regexp.last_match(1).extract_shortener
|
612
431
|
link_info = Regexp.last_match(2).strip
|
432
|
+
|
613
433
|
@link_text ||= link_info
|
614
434
|
terms = link_info + additional_terms
|
615
435
|
terms.strip!
|
@@ -617,60 +437,7 @@ module SL
|
|
617
437
|
if SL::Searches.valid_search?(type) || type =~ /^(\S+\.)+\S+$/
|
618
438
|
if type && terms && !terms.empty?
|
619
439
|
# Iterate through custom searches for a match, perform search if matched
|
620
|
-
|
621
|
-
next unless type == k
|
622
|
-
|
623
|
-
@link_text = terms if @link_text == ""
|
624
|
-
v = parse_arguments(v, { no_restore: true })
|
625
|
-
if v =~ %r{^(/|http)}i
|
626
|
-
type = "r"
|
627
|
-
tokens = v.scan(/\$term\d+[ds]?/).sort.uniq
|
628
|
-
|
629
|
-
if !tokens.empty?
|
630
|
-
highest_token = 0
|
631
|
-
tokens.each do |token|
|
632
|
-
t = Regexp.last_match(1)
|
633
|
-
highest_token = t.to_i if token =~ /(\d+)d?$/ && t.to_i > highest_token
|
634
|
-
end
|
635
|
-
terms_p = terms.split(/ +/)
|
636
|
-
if terms_p.length > highest_token
|
637
|
-
remainder = terms_p[highest_token - 1..].join(" ")
|
638
|
-
terms_p = terms_p[0..highest_token - 2]
|
639
|
-
terms_p.push(remainder)
|
640
|
-
end
|
641
|
-
tokens.each do |t|
|
642
|
-
next unless t =~ /(\d+)d?$/
|
643
|
-
|
644
|
-
int = Regexp.last_match(1).to_i - 1
|
645
|
-
replacement = terms_p[int]
|
646
|
-
|
647
|
-
re_down = case t
|
648
|
-
when /d$/
|
649
|
-
replacement.downcase!
|
650
|
-
""
|
651
|
-
when /s$/
|
652
|
-
replacement.slugify!
|
653
|
-
""
|
654
|
-
else
|
655
|
-
"(?!d|s)"
|
656
|
-
end
|
657
|
-
v.gsub!(/#{Regexp.escape(t) + re_down}/, replacement.url_encode)
|
658
|
-
end
|
659
|
-
terms = v
|
660
|
-
else
|
661
|
-
terms = v.gsub(/\$term[ds]?/i) do |mtch|
|
662
|
-
terms.downcase! if mtch =~ /d$/i
|
663
|
-
terms.slugify! if mtch =~ /s$/i
|
664
|
-
terms.url_encode
|
665
|
-
end
|
666
|
-
end
|
667
|
-
else
|
668
|
-
type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
669
|
-
terms = "site:#{v} #{terms}"
|
670
|
-
end
|
671
|
-
|
672
|
-
break
|
673
|
-
end
|
440
|
+
type, terms = custom_search(type, terms)
|
674
441
|
end
|
675
442
|
|
676
443
|
# if contains TLD, use site-specific search
|
@@ -681,6 +448,7 @@ module SL
|
|
681
448
|
@search_count ||= 0
|
682
449
|
@search_count += 1
|
683
450
|
|
451
|
+
SL.add_query(query) if query
|
684
452
|
@url, title, @link_text = do_search(type, terms, @link_text, @search_count)
|
685
453
|
else
|
686
454
|
SL.add_error("Invalid search#{SL::Searches.did_you_mean(type)}", input)
|
@@ -695,9 +463,11 @@ module SL
|
|
695
463
|
"t"
|
696
464
|
end
|
697
465
|
@link_text = input.sub(/^[tfilm]/, "")
|
466
|
+
SL.add_query(query) if query
|
698
467
|
@url, title = SL::SocialSearch.social_handle(type, @link_text)
|
699
468
|
@link_text = title
|
700
469
|
else
|
470
|
+
SL.add_query(query) if query
|
701
471
|
@link_text ||= input
|
702
472
|
@url, title, @link_text = SL.ddg(input, @link_text)
|
703
473
|
end
|
@@ -731,5 +501,199 @@ module SL
|
|
731
501
|
end
|
732
502
|
end
|
733
503
|
end
|
504
|
+
|
505
|
+
private
|
506
|
+
|
507
|
+
def add_title(link_info)
|
508
|
+
@url = link_info
|
509
|
+
title = SL::URL.title(@url)
|
510
|
+
@link_text = title
|
511
|
+
|
512
|
+
if @ref_title
|
513
|
+
unless @links.key? @url
|
514
|
+
@links[@url] = @link_text
|
515
|
+
SL.add_footer SL.make_link(:ref_title, @link_text, @url, title: title, force_title: false)
|
516
|
+
end
|
517
|
+
@delete_line = true
|
518
|
+
elsif SL.config["inline"]
|
519
|
+
res = SL.make_link(:inline, @link_text, @url, title: title, force_title: false)
|
520
|
+
@cursor_difference += SL.match_length - res.length
|
521
|
+
SL.match_length = res.length
|
522
|
+
SL.add_report("#{@match_string} => #{@url}")
|
523
|
+
res
|
524
|
+
else
|
525
|
+
unless @links.key? @url
|
526
|
+
@highest_marker += 1
|
527
|
+
@links[@url] = format("%<pre>s%<m>04d", pre: @prefix, m: @highest_marker)
|
528
|
+
SL.add_footer SL.make_link(:ref_title, @links[@url], @url, title: title, force_title: false)
|
529
|
+
end
|
530
|
+
|
531
|
+
type = SL.config["inline"] ? :inline : :ref_link
|
532
|
+
res = SL.make_link(type, @link_text, @links[@url], title: false, force_title: false)
|
533
|
+
@cursor_difference += SL.match_length - res.length
|
534
|
+
SL.match_length = res.length
|
535
|
+
SL.add_report("#{@match_string} => #{@url}")
|
536
|
+
res
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
# Parse arguments in the input string
|
541
|
+
#
|
542
|
+
# @param string [String] the string to parse
|
543
|
+
# @param opt [Hash] the options to parse
|
544
|
+
# @option opt [Boolean] :only_meta (false) whether to skip flags
|
545
|
+
# @option opt [Boolean] :no_restore (false) whether to restore previous config
|
546
|
+
# @return [String] the parsed string
|
547
|
+
#
|
548
|
+
def parse_arguments(string, opt = {})
|
549
|
+
input = string.dup
|
550
|
+
return "" if input.nil?
|
551
|
+
|
552
|
+
skip_flags = opt[:only_meta] || false
|
553
|
+
no_restore = opt[:no_restore] || false
|
554
|
+
restore_prev_config unless no_restore
|
555
|
+
|
556
|
+
input.parse_flags! unless skip_flags
|
557
|
+
|
558
|
+
options = %w[debug country_code inline prefix_random include_titles remove_seo validate_links complete_bare]
|
559
|
+
options.each do |o|
|
560
|
+
if input =~ /^ *#{o}:\s+(\S+)$/
|
561
|
+
val = Regexp.last_match(1).strip
|
562
|
+
|
563
|
+
if val.is_a?(String)
|
564
|
+
value = true if val =~ /true/i
|
565
|
+
value = false if val =~ /false/i
|
566
|
+
end
|
567
|
+
val = value if value
|
568
|
+
SL.config[o] = val
|
569
|
+
warn "\r\033[0KGlobal config: #{o} = #{SL.config[o]}\n" unless SILENT
|
570
|
+
end
|
571
|
+
|
572
|
+
next if skip_flags
|
573
|
+
|
574
|
+
while input =~ /^#{o}:\s+(.*?)$/ || input =~ /--(no-)?#{o}/
|
575
|
+
next unless input =~ /--(no-)?#{o}/ && !skip_flags
|
576
|
+
|
577
|
+
unless SL.prev_config.key? o
|
578
|
+
SL.prev_config[o] = SL.config[o]
|
579
|
+
bool = Regexp.last_match(1).nil? || Regexp.last_match(1) == "" ? true : false
|
580
|
+
SL.config[o] = bool
|
581
|
+
$stderr.print "\r\033[0KLine config: #{o} = #{SL.config[o]}\n" unless SILENT
|
582
|
+
end
|
583
|
+
input.sub!(/\s?--(no-)?#{o}/, "")
|
584
|
+
end
|
585
|
+
end
|
586
|
+
SL.clipboard ? string : input
|
587
|
+
end
|
588
|
+
|
589
|
+
# Parse commands from the given input string
|
590
|
+
#
|
591
|
+
# @param input [String] the input string
|
592
|
+
def parse_commands(input)
|
593
|
+
# Handle commands like help or docs
|
594
|
+
return unless input.strip =~ /^!?(h(elp)?|wiki|docs?|v(er(s(ion)?)?)?|up(date|grade))$/
|
595
|
+
|
596
|
+
case input.strip
|
597
|
+
when /^!?help$/i
|
598
|
+
if SILENT
|
599
|
+
help_dialog
|
600
|
+
else
|
601
|
+
$stdout.puts SL.version_check.to_s
|
602
|
+
$stdout.puts "See https://github.com/ttscoff/searchlink/wiki for help"
|
603
|
+
end
|
604
|
+
print input
|
605
|
+
when /^!?(wiki|docs)$/i
|
606
|
+
warn "Opening wiki in browser"
|
607
|
+
`open https://github.com/ttscoff/searchlink/wiki`
|
608
|
+
when /^!?v(er(s(ion)?)?)?$/
|
609
|
+
print "[#{SL.version_check}]"
|
610
|
+
when /^!?up(date|grade)$/
|
611
|
+
SL.update_searchlink
|
612
|
+
print SL.output.join("")
|
613
|
+
end
|
614
|
+
Process.exit 0
|
615
|
+
end
|
616
|
+
|
617
|
+
def create_footnote(mtch)
|
618
|
+
if mtch[1].nil? || mtch[1] == ""
|
619
|
+
match
|
620
|
+
else
|
621
|
+
note = mtch[1].strip
|
622
|
+
@footnote_counter += 1
|
623
|
+
ref = if !@link_text.empty? && @link_text.scan(/\s/).empty?
|
624
|
+
@link_text
|
625
|
+
else
|
626
|
+
format("%<p>sfn%<c>04d", p: @prefix, c: @footnote_counter)
|
627
|
+
end
|
628
|
+
SL.add_footer "[^#{ref}]: #{note}"
|
629
|
+
res = "[^#{ref}]"
|
630
|
+
@cursor_difference += (SL.match_length - res.length)
|
631
|
+
SL.match_length = res.length
|
632
|
+
SL.add_report("#{@match_string} => Footnote #{ref}")
|
633
|
+
res
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
637
|
+
def custom_search(search_type, search_terms)
|
638
|
+
SL.config["custom_site_searches"].each do |k, v|
|
639
|
+
next unless search_type == k
|
640
|
+
|
641
|
+
@link_text = search_terms if !SL.titleize && @link_text == ""
|
642
|
+
v = parse_arguments(v, { no_restore: true })
|
643
|
+
query, v = v.extract_query({})
|
644
|
+
|
645
|
+
SL.add_query(query)
|
646
|
+
|
647
|
+
if v =~ %r{^(/|http)}i
|
648
|
+
search_type = "r"
|
649
|
+
tokens = v.scan(/\$term\d+[ds]?/).sort.uniq
|
650
|
+
|
651
|
+
if !tokens.empty?
|
652
|
+
highest_token = 0
|
653
|
+
tokens.each do |token|
|
654
|
+
if token =~ /(\d+)[ds]?$/ && Regexp.last_match(1).to_i > highest_token
|
655
|
+
highest_token = Regexp.last_match(1).to_i
|
656
|
+
end
|
657
|
+
end
|
658
|
+
terms_p = search_terms.split(/ +/)
|
659
|
+
if terms_p.length > highest_token
|
660
|
+
remainder = terms_p[highest_token - 1..].join(" ")
|
661
|
+
terms_p = terms_p[0..highest_token - 2]
|
662
|
+
terms_p.push(remainder)
|
663
|
+
end
|
664
|
+
tokens.each do |t|
|
665
|
+
next unless t =~ /(\d+)[ds]?$/
|
666
|
+
|
667
|
+
int = Regexp.last_match(1).to_i - 1
|
668
|
+
replacement = terms_p[int]
|
669
|
+
case t
|
670
|
+
when /d$/
|
671
|
+
replacement.downcase!
|
672
|
+
re_down = ""
|
673
|
+
when /s$/
|
674
|
+
replacement.slugify!
|
675
|
+
re_down = ""
|
676
|
+
else
|
677
|
+
re_down = "(?!d|s)"
|
678
|
+
end
|
679
|
+
v.gsub!(/#{Regexp.escape(t) + re_down}/, replacement.url_encode)
|
680
|
+
end
|
681
|
+
search_terms = v
|
682
|
+
else
|
683
|
+
search_terms = v.gsub(/\$term[ds]?/i) do |mtch|
|
684
|
+
search_terms.downcase! if mtch =~ /d$/i
|
685
|
+
search_terms.slugify! if mtch =~ /s$/i
|
686
|
+
search_terms.url_encode
|
687
|
+
end
|
688
|
+
end
|
689
|
+
else
|
690
|
+
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
691
|
+
search_terms = "site:#{v} #{search_terms}"
|
692
|
+
end
|
693
|
+
|
694
|
+
break
|
695
|
+
end
|
696
|
+
[search_type, search_terms]
|
697
|
+
end
|
734
698
|
end
|
735
699
|
end
|
@@ -68,8 +68,13 @@ module SL
|
|
68
68
|
## @return [Array] [url, title, date]
|
69
69
|
##
|
70
70
|
def search_safari_bookmarks(terms)
|
71
|
-
data = `plutil -
|
71
|
+
data = `plutil -extract Children xml1 -o - ~/Library/Safari/Bookmarks.plist`.strip
|
72
|
+
|
73
|
+
data.gsub!(%r{<key>Data</key>\s+<data>(.+?)</data>\n}m, "")
|
74
|
+
data.gsub!(/\t/, "")
|
75
|
+
|
72
76
|
parent = Plist.parse_xml(data)
|
77
|
+
|
73
78
|
results = get_safari_bookmarks(parent, terms)
|
74
79
|
return false if results.empty?
|
75
80
|
|
@@ -45,12 +45,11 @@ module SL
|
|
45
45
|
],
|
46
46
|
config: [
|
47
47
|
{
|
48
|
-
description:
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
"History and Chrome bookmarks)"].join(" "),
|
48
|
+
description: ["Remove or comment (with #) history searches you don't want",
|
49
|
+
"performed by `!h`. You can force-enable them per search, e.g.",
|
50
|
+
"`!hsh` (Safari History only), `!hcb` (Chrome Bookmarks only)",
|
51
|
+
"etc. Multiple types can be strung together: !hshcb (Safari",
|
52
|
+
"History and Chrome bookmarks)"].join(" "),
|
54
53
|
required: false,
|
55
54
|
key: "history_types",
|
56
55
|
value: %w[
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SL
|
4
|
+
# is.gd link shortening
|
5
|
+
class TinyurlSearch
|
6
|
+
class << self
|
7
|
+
def settings
|
8
|
+
{
|
9
|
+
trigger: "tiny",
|
10
|
+
searches: [
|
11
|
+
["tiny", "TinyURL Shorten"]
|
12
|
+
],
|
13
|
+
config: [
|
14
|
+
{
|
15
|
+
key: "tinyurl_access_token",
|
16
|
+
value: "",
|
17
|
+
required: true,
|
18
|
+
description: "Generate a tinyurl API key at https://tinyurl.ph/developers (login required)"
|
19
|
+
}
|
20
|
+
]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def search(_, search_terms, link_text)
|
25
|
+
if SL::URL.url?(search_terms)
|
26
|
+
link = search_terms
|
27
|
+
else
|
28
|
+
link, title, link_text = SL.ddg(search_terms, link_text)
|
29
|
+
end
|
30
|
+
|
31
|
+
url = shorten(link)
|
32
|
+
title = SL::URL.title(link) if title.nil? || title.empty?
|
33
|
+
link_text = title if (link_text.nil? || link_text.empty?) && !SL.titleize
|
34
|
+
format_response(url, link, link_text)
|
35
|
+
end
|
36
|
+
|
37
|
+
def shorten(url)
|
38
|
+
return false unless tinyurl_config?
|
39
|
+
|
40
|
+
headers = {
|
41
|
+
"Content-Type" => "application/json",
|
42
|
+
"Authorization" => "Bearer #{SL.config['tinyurl_access_token']}"
|
43
|
+
}
|
44
|
+
data_obj = {
|
45
|
+
"url" => url
|
46
|
+
}
|
47
|
+
data = Curl::Json.new("https://tinyurl.ph/api/url/add", data: data_obj.to_json, headers: headers, symbolize_names: true)
|
48
|
+
|
49
|
+
if data.json[:error].positive?
|
50
|
+
SL.add_error("Error creating tinyurl", data.json[:error])
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
|
54
|
+
data.json[:shorturl]
|
55
|
+
end
|
56
|
+
|
57
|
+
def tinyurl_config?
|
58
|
+
return true if SL.config["tinyurl_access_token"] && !SL.config["tinyurl_access_token"].empty?
|
59
|
+
|
60
|
+
SL.add_error("TinyURL not configured", "Missing access token")
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
def format_response(link, original_url, link_text)
|
65
|
+
rtitle = SL::URL.title(original_url)
|
66
|
+
[link, rtitle, link_text == "" && !SL.titleize ? rtitle : link_text]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
SL::Searches.register "tiny", :search, self
|
71
|
+
end
|
72
|
+
end
|
data/lib/searchlink/searches.rb
CHANGED
data/lib/searchlink/string.rb
CHANGED
@@ -37,6 +37,73 @@ module SL
|
|
37
37
|
replace scrub
|
38
38
|
end
|
39
39
|
|
40
|
+
# Extract query string from search string
|
41
|
+
def extract_query(known_queries = {})
|
42
|
+
string = gsub(/\?((\S+?)=(\S+?)(?=&|$|\s))+/) do |mtch|
|
43
|
+
tokens = mtch.sub(/^\?/, "").split("&")
|
44
|
+
tokens.each do |token|
|
45
|
+
key, value = token.split("=")
|
46
|
+
|
47
|
+
known_queries[key] = value
|
48
|
+
end
|
49
|
+
|
50
|
+
""
|
51
|
+
end.gsub(/ +/, " ").strip
|
52
|
+
|
53
|
+
[known_queries, string]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extract a shortner from a string
|
57
|
+
def extract_shortener
|
58
|
+
return self unless self =~ /_[ibt]$/i
|
59
|
+
|
60
|
+
shortener = split(/_/).last
|
61
|
+
SL.shortener = case shortener
|
62
|
+
when /i/i
|
63
|
+
:isgd
|
64
|
+
when /b/i
|
65
|
+
:bitly
|
66
|
+
when /t/i
|
67
|
+
:tinyurl
|
68
|
+
else
|
69
|
+
:none
|
70
|
+
end
|
71
|
+
|
72
|
+
sub(/_[ibt]$/i, "")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Destructive version of #extract_shortener
|
76
|
+
# @see #extract_shortener
|
77
|
+
# @return [String] The string without the shortener
|
78
|
+
def extract_shortener!
|
79
|
+
replace extract_shortener
|
80
|
+
end
|
81
|
+
|
82
|
+
# Format and append a query string
|
83
|
+
#
|
84
|
+
# @return [String] The formatted query string
|
85
|
+
#
|
86
|
+
def add_query_string
|
87
|
+
return self if SL.query.empty?
|
88
|
+
|
89
|
+
query = SL.query.map { |k, v| "#{k}=#{v}" }.join("&")
|
90
|
+
|
91
|
+
query = if self =~ /\?[^= ]+=\S+/
|
92
|
+
"&#{query}"
|
93
|
+
else
|
94
|
+
"?#{query}"
|
95
|
+
end
|
96
|
+
|
97
|
+
"#{self}#{query}"
|
98
|
+
end
|
99
|
+
|
100
|
+
# Destructive version of #add_query_string
|
101
|
+
# @see #add_query_string
|
102
|
+
# @return [String] The formatted query string
|
103
|
+
def add_query_string!
|
104
|
+
replace add_query_string
|
105
|
+
end
|
106
|
+
|
40
107
|
# URL Encode string
|
41
108
|
#
|
42
109
|
# @return [String] url encoded string
|
data/lib/searchlink/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
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.85
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-03-
|
10
|
+
date: 2025-03-30 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: base64
|
@@ -141,14 +141,14 @@ dependencies:
|
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: 3.7.
|
144
|
+
version: 3.7.2
|
145
145
|
type: :development
|
146
146
|
prerelease: false
|
147
147
|
version_requirements: !ruby/object:Gem::Requirement
|
148
148
|
requirements:
|
149
149
|
- - "~>"
|
150
150
|
- !ruby/object:Gem::Version
|
151
|
-
version: 3.7.
|
151
|
+
version: 3.7.2
|
152
152
|
- !ruby/object:Gem::Dependency
|
153
153
|
name: rake
|
154
154
|
requirement: !ruby/object:Gem::Requirement
|
@@ -309,6 +309,7 @@ files:
|
|
309
309
|
- lib/searchlink/searches/spelling.rb
|
310
310
|
- lib/searchlink/searches/spotlight.rb
|
311
311
|
- lib/searchlink/searches/stackoverflow.rb
|
312
|
+
- lib/searchlink/searches/tinyurl.rb
|
312
313
|
- lib/searchlink/searches/tmdb.rb
|
313
314
|
- lib/searchlink/searches/twitter.rb
|
314
315
|
- lib/searchlink/searches/wikipedia.rb
|