searchlink 2.3.59
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/searchlink +84 -0
- data/lib/searchlink/array.rb +7 -0
- data/lib/searchlink/config.rb +230 -0
- data/lib/searchlink/curl/html.rb +482 -0
- data/lib/searchlink/curl/json.rb +90 -0
- data/lib/searchlink/curl.rb +7 -0
- data/lib/searchlink/help.rb +103 -0
- data/lib/searchlink/output.rb +270 -0
- data/lib/searchlink/parse.rb +668 -0
- data/lib/searchlink/plist.rb +213 -0
- data/lib/searchlink/search.rb +70 -0
- data/lib/searchlink/searches/amazon.rb +25 -0
- data/lib/searchlink/searches/applemusic.rb +123 -0
- data/lib/searchlink/searches/bitly.rb +50 -0
- data/lib/searchlink/searches/definition.rb +67 -0
- data/lib/searchlink/searches/duckduckgo.rb +167 -0
- data/lib/searchlink/searches/github.rb +245 -0
- data/lib/searchlink/searches/google.rb +67 -0
- data/lib/searchlink/searches/helpers/chromium.rb +318 -0
- data/lib/searchlink/searches/helpers/firefox.rb +135 -0
- data/lib/searchlink/searches/helpers/safari.rb +133 -0
- data/lib/searchlink/searches/history.rb +166 -0
- data/lib/searchlink/searches/hook.rb +77 -0
- data/lib/searchlink/searches/itunes.rb +97 -0
- data/lib/searchlink/searches/lastfm.rb +41 -0
- data/lib/searchlink/searches/lyrics.rb +91 -0
- data/lib/searchlink/searches/pinboard.rb +183 -0
- data/lib/searchlink/searches/social.rb +105 -0
- data/lib/searchlink/searches/software.rb +27 -0
- data/lib/searchlink/searches/spelling.rb +59 -0
- data/lib/searchlink/searches/spotlight.rb +28 -0
- data/lib/searchlink/searches/stackoverflow.rb +31 -0
- data/lib/searchlink/searches/tmdb.rb +52 -0
- data/lib/searchlink/searches/twitter.rb +46 -0
- data/lib/searchlink/searches/wikipedia.rb +33 -0
- data/lib/searchlink/searches/youtube.rb +48 -0
- data/lib/searchlink/searches.rb +194 -0
- data/lib/searchlink/semver.rb +140 -0
- data/lib/searchlink/string.rb +469 -0
- data/lib/searchlink/url.rb +153 -0
- data/lib/searchlink/util.rb +87 -0
- data/lib/searchlink/version.rb +93 -0
- data/lib/searchlink/which.rb +175 -0
- data/lib/searchlink.rb +66 -0
- data/lib/tokens.rb +3 -0
- metadata +299 -0
@@ -0,0 +1,668 @@
|
|
1
|
+
module SL
|
2
|
+
class SearchLink
|
3
|
+
# Parse arguments in the input string
|
4
|
+
#
|
5
|
+
# @param string [String] the string to parse
|
6
|
+
# @param opt [Hash] the options to parse
|
7
|
+
# @option opt [Boolean] :only_meta (false) whether to skip flags
|
8
|
+
# @option opt [Boolean] :no_restore (false) whether to restore previous config
|
9
|
+
# @return [String] the parsed string
|
10
|
+
#
|
11
|
+
def parse_arguments(string, opt={})
|
12
|
+
input = string.dup
|
13
|
+
return "" if input.nil?
|
14
|
+
|
15
|
+
skip_flags = opt[:only_meta] || false
|
16
|
+
no_restore = opt[:no_restore] || false
|
17
|
+
restore_prev_config unless no_restore
|
18
|
+
|
19
|
+
input.parse_flags! unless skip_flags
|
20
|
+
|
21
|
+
options = %w[debug country_code inline prefix_random include_titles remove_seo validate_links]
|
22
|
+
options.each do |o|
|
23
|
+
if input =~ /^ *#{o}:\s+(\S+)$/
|
24
|
+
val = Regexp.last_match(1).strip
|
25
|
+
val = true if val =~ /true/i
|
26
|
+
val = false if val =~ /false/i
|
27
|
+
SL.config[o] = val
|
28
|
+
$stderr.print "\r\033[0KGlobal config: #{o} = #{SL.config[o]}\n" unless SILENT
|
29
|
+
end
|
30
|
+
|
31
|
+
next if skip_flags
|
32
|
+
|
33
|
+
while input =~ /^#{o}:\s+(.*?)$/ || input =~ /--(no-)?#{o}/
|
34
|
+
next unless input =~ /--(no-)?#{o}/ && !skip_flags
|
35
|
+
|
36
|
+
unless SL.prev_config.key? o
|
37
|
+
SL.prev_config[o] = SL.config[o]
|
38
|
+
bool = Regexp.last_match(1).nil? || Regexp.last_match(1) == '' ? true : false
|
39
|
+
SL.config[o] = bool
|
40
|
+
$stderr.print "\r\033[0KLine config: #{o} = #{SL.config[o]}\n" unless SILENT
|
41
|
+
end
|
42
|
+
input.sub!(/\s?--(no-)?#{o}/, '')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
SL.clipboard ? string : input
|
46
|
+
end
|
47
|
+
|
48
|
+
# Parse commands from the given input string
|
49
|
+
#
|
50
|
+
# @param input [String] the input string
|
51
|
+
def parse_commands(input)
|
52
|
+
# Handle commands like help or docs
|
53
|
+
return unless input.strip =~ /^!?(h(elp)?|wiki|docs?|v(er(s(ion)?)?)?|up(date|grade))$/
|
54
|
+
|
55
|
+
case input.strip
|
56
|
+
when /^!?help$/i
|
57
|
+
if SILENT
|
58
|
+
help_dialog # %x{open http://brettterpstra.com/projects/searchlink/}
|
59
|
+
else
|
60
|
+
$stdout.puts SL.version_check.to_s
|
61
|
+
$stdout.puts 'See https://github.com/ttscoff/searchlink/wiki for help'
|
62
|
+
end
|
63
|
+
print input
|
64
|
+
when /^!?(wiki|docs)$/i
|
65
|
+
warn 'Opening wiki in browser'
|
66
|
+
`open https://github.com/ttscoff/searchlink/wiki`
|
67
|
+
when /^!?v(er(s(ion)?)?)?$/
|
68
|
+
print "[#{SL.version_check}]"
|
69
|
+
when /^!?up(date|grade)$/
|
70
|
+
SL.update_searchlink
|
71
|
+
print SL.output.join('')
|
72
|
+
end
|
73
|
+
Process.exit 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse(input)
|
77
|
+
SL.output = []
|
78
|
+
return false if input.empty?
|
79
|
+
|
80
|
+
parse_arguments(input, { only_meta: true })
|
81
|
+
SL.originput = input.dup
|
82
|
+
|
83
|
+
parse_commands(input)
|
84
|
+
|
85
|
+
SL.config['inline'] = true if input.scan(/\]\(/).length == 1 && input.split(/\n/).length == 1
|
86
|
+
SL.errors = {}
|
87
|
+
SL.report = []
|
88
|
+
|
89
|
+
# Check for new version
|
90
|
+
latest_version = SL.new_version?
|
91
|
+
if latest_version
|
92
|
+
SL.add_output("<!-- v#{latest_version} available, run SearchLink on the word 'update' to install. -->")
|
93
|
+
end
|
94
|
+
|
95
|
+
links = {}
|
96
|
+
SL.footer = []
|
97
|
+
counter_links = 0
|
98
|
+
counter_errors = 0
|
99
|
+
|
100
|
+
input.sub!(/\n?<!-- Report:.*?-->\n?/m, '')
|
101
|
+
input.sub!(/\n?<!-- Errors:.*?-->\n?/m, '')
|
102
|
+
|
103
|
+
input.scan(/\[(.*?)\]:\s+(.*?)\n/).each { |match| links[match[1].strip] = match[0] }
|
104
|
+
|
105
|
+
prefix = if SL.config['prefix_random']
|
106
|
+
if input =~ /\[(\d{4}-)\d+\]: \S+/
|
107
|
+
Regexp.last_match(1)
|
108
|
+
else
|
109
|
+
format('%04d-', rand(9999))
|
110
|
+
end
|
111
|
+
else
|
112
|
+
''
|
113
|
+
end
|
114
|
+
|
115
|
+
highest_marker = 0
|
116
|
+
input.scan(/^\s{,3}\[(?:#{prefix})?(\d+)\]: /).each do
|
117
|
+
m = Regexp.last_match
|
118
|
+
highest_marker = m[1].to_i if m[1].to_i > highest_marker
|
119
|
+
end
|
120
|
+
|
121
|
+
footnote_counter = 0
|
122
|
+
input.scan(/^\s{,3}\[\^(?:#{prefix})?fn(\d+)\]: /).each do
|
123
|
+
m = Regexp.last_match
|
124
|
+
footnote_counter = m[1].to_i if m[1].to_i > footnote_counter
|
125
|
+
end
|
126
|
+
|
127
|
+
if input =~ /\[(.*?)\]\((.*?)\)/
|
128
|
+
lines = input.split(/\n/)
|
129
|
+
out = []
|
130
|
+
|
131
|
+
total_links = input.scan(/\[(.*?)\]\((.*?)\)/).length
|
132
|
+
in_code_block = false
|
133
|
+
line_difference = 0
|
134
|
+
lines.each_with_index do |line, num|
|
135
|
+
SL.line_num = num - line_difference
|
136
|
+
cursor_difference = 0
|
137
|
+
# ignore links in code blocks
|
138
|
+
if line =~ /^( {4,}|\t+)[^*+\-]/
|
139
|
+
out.push(line)
|
140
|
+
next
|
141
|
+
end
|
142
|
+
if line =~ /^[~`]{3,}/
|
143
|
+
if in_code_block
|
144
|
+
in_code_block = false
|
145
|
+
out.push(line)
|
146
|
+
next
|
147
|
+
else
|
148
|
+
in_code_block = true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
if in_code_block
|
152
|
+
out.push(line)
|
153
|
+
next
|
154
|
+
end
|
155
|
+
|
156
|
+
delete_line = false
|
157
|
+
|
158
|
+
search_count = 0
|
159
|
+
|
160
|
+
line.gsub!(/\[(.*?)\]\((.*?)\)/) do |match|
|
161
|
+
this_match = Regexp.last_match
|
162
|
+
SL.match_column = this_match.begin(0) - cursor_difference
|
163
|
+
match_string = this_match.to_s
|
164
|
+
SL.match_length = match_string.length
|
165
|
+
match_before = this_match.pre_match
|
166
|
+
|
167
|
+
invalid_search = false
|
168
|
+
ref_title = false
|
169
|
+
|
170
|
+
if match_before.scan(/(^|[^\\])`/).length.odd?
|
171
|
+
SL.add_report("Match '#{match_string}' within an inline code block")
|
172
|
+
invalid_search = true
|
173
|
+
end
|
174
|
+
|
175
|
+
counter_links += 1
|
176
|
+
unless SILENT
|
177
|
+
$stderr.print("\033[0K\rProcessed: #{counter_links} of #{total_links}, #{counter_errors} errors. ")
|
178
|
+
end
|
179
|
+
|
180
|
+
link_text = this_match[1] || ''
|
181
|
+
link_info = parse_arguments(this_match[2].strip).strip || ''
|
182
|
+
|
183
|
+
if link_text.strip == '' && link_info =~ /".*?"/
|
184
|
+
link_info.gsub!(/"(.*?)"/) do
|
185
|
+
m = Regexp.last_match(1)
|
186
|
+
link_text = m if link_text == ''
|
187
|
+
%("#")
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
link_info.gsub!(/<(.*?)>/) do
|
192
|
+
%(%22#{Regexp.last_match(1)}%22)
|
193
|
+
end
|
194
|
+
|
195
|
+
if link_info.strip =~ /:$/ && line.strip == match
|
196
|
+
ref_title = true
|
197
|
+
link_info.sub!(/\s*:\s*$/, '')
|
198
|
+
end
|
199
|
+
|
200
|
+
unless !link_text.empty? || !link_info.sub(/^[!\^]\S+/, '').strip.empty?
|
201
|
+
SL.add_error('No input', match)
|
202
|
+
counter_errors += 1
|
203
|
+
invalid_search = true
|
204
|
+
end
|
205
|
+
|
206
|
+
if link_info =~ /^!(\S+)/
|
207
|
+
search_type = Regexp.last_match(1)
|
208
|
+
unless SL::Searches.valid_search?(search_type) || search_type =~ /^(\S+\.)+\S+$/
|
209
|
+
SL.add_error("Invalid search#{SL::Searches.did_you_mean(search_type)}", match)
|
210
|
+
invalid_search = true
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
if invalid_search
|
215
|
+
match
|
216
|
+
elsif link_info =~ /^\^(.+)/
|
217
|
+
m = Regexp.last_match
|
218
|
+
if m[1].nil? || m[1] == ''
|
219
|
+
match
|
220
|
+
else
|
221
|
+
note = m[1].strip
|
222
|
+
footnote_counter += 1
|
223
|
+
ref = if !link_text.empty? && link_text.scan(/\s/).empty?
|
224
|
+
link_text
|
225
|
+
else
|
226
|
+
format('%<p>sfn%<c>04d', p: prefix, c: footnote_counter)
|
227
|
+
end
|
228
|
+
SL.add_footer "[^#{ref}]: #{note}"
|
229
|
+
res = "[^#{ref}]"
|
230
|
+
cursor_difference += (SL.match_length - res.length)
|
231
|
+
SL.match_length = res.length
|
232
|
+
SL.add_report("#{match_string} => Footnote #{ref}")
|
233
|
+
res
|
234
|
+
end
|
235
|
+
# Handle [](URL) and [%](URL), filling in title
|
236
|
+
elsif (link_text == '' || link_text == '%') && SL::URL.url?(link_info)
|
237
|
+
url = link_info
|
238
|
+
title = SL::URL.title(link_info)
|
239
|
+
link_text = title
|
240
|
+
|
241
|
+
if ref_title
|
242
|
+
unless links.key? url
|
243
|
+
links[url] = link_text
|
244
|
+
SL.add_footer SL.make_link(:ref_title, link_text, url, title: title, force_title: false)
|
245
|
+
end
|
246
|
+
delete_line = true
|
247
|
+
elsif SL.config['inline']
|
248
|
+
res = SL.make_link(:inline, link_text, url, title: title, force_title: false)
|
249
|
+
cursor_difference += SL.match_length - res.length
|
250
|
+
SL.match_length = res.length
|
251
|
+
SL.add_report("#{match_string} => #{url}")
|
252
|
+
res
|
253
|
+
else
|
254
|
+
unless links.key? url
|
255
|
+
highest_marker += 1
|
256
|
+
links[url] = format('%<pre>s%<m>04d', pre: prefix, m: highest_marker)
|
257
|
+
SL.add_footer SL.make_link(:ref_title, links[url], url, title: title, force_title: false)
|
258
|
+
end
|
259
|
+
|
260
|
+
type = SL.config['inline'] ? :inline : :ref_link
|
261
|
+
res = SL.make_link(type, link_text, links[url], title: false, force_title: false)
|
262
|
+
cursor_difference += SL.match_length - res.length
|
263
|
+
SL.match_length = res.length
|
264
|
+
SL.add_report("#{match_string} => #{url}")
|
265
|
+
res
|
266
|
+
end
|
267
|
+
elsif (link_text == '' && link_info == '') || SL::URL.url?(link_info)
|
268
|
+
SL.add_error('Invalid search', match) unless SL::URL.url?(link_info)
|
269
|
+
match
|
270
|
+
else
|
271
|
+
link_info = link_text if !link_text.empty? && link_info == ''
|
272
|
+
|
273
|
+
search_type = ''
|
274
|
+
search_terms = ''
|
275
|
+
link_only = false
|
276
|
+
SL.clipboard = false
|
277
|
+
SL.titleize = SL.config['empty_uses_page_title']
|
278
|
+
|
279
|
+
if link_info =~ /^(?:[!\^](\S+))\s*(.*)$/
|
280
|
+
m = Regexp.last_match
|
281
|
+
|
282
|
+
search_type = m[1].nil? ? (SL::GoogleSearch.test_for_key ? 'gg' : 'g') : m[1]
|
283
|
+
|
284
|
+
search_terms = m[2].gsub(/(^["']|["']$)/, '')
|
285
|
+
search_terms.strip!
|
286
|
+
|
287
|
+
# if the link text is just '%' replace with title regardless of config settings
|
288
|
+
if link_text == '%' && search_terms && !search_terms.empty?
|
289
|
+
SL.titleize = true
|
290
|
+
link_text = ''
|
291
|
+
end
|
292
|
+
|
293
|
+
search_terms = link_text if search_terms == ''
|
294
|
+
|
295
|
+
# if the input starts with a +, append it to the link text as the search terms
|
296
|
+
search_terms = "#{link_text} #{search_terms.strip.sub(/^\+\s*/, '')}" if search_terms.strip =~ /^\+[^+]/
|
297
|
+
|
298
|
+
# if the end of input contain "^", copy to clipboard instead of STDOUT
|
299
|
+
SL.clipboard = true if search_terms =~ /(!!)?\^(!!)?$/
|
300
|
+
|
301
|
+
# if the end of input contains "!!", only print the url
|
302
|
+
link_only = true if search_terms =~ /!!\^?$/
|
303
|
+
|
304
|
+
search_terms.sub!(/(!!)?\^?(!!)?$/,"")
|
305
|
+
|
306
|
+
elsif link_info =~ /^!/
|
307
|
+
search_word = link_info.match(/^!(\S+)/)
|
308
|
+
|
309
|
+
if search_word && SL::Searches.valid_search?(search_word[1])
|
310
|
+
search_type = search_word[1] unless search_word.nil?
|
311
|
+
search_terms = link_text
|
312
|
+
elsif search_word && search_word[1] =~ /^(\S+\.)+\S+$/
|
313
|
+
search_type = SL::GoogleSearch.test_for_key ? 'gg' : 'g'
|
314
|
+
puts SL::GoogleSearch.test_for_key
|
315
|
+
search_terms = "site:#{search_word[1]} #{link_text}"
|
316
|
+
else
|
317
|
+
SL.add_error("Invalid search#{SL::Searches.did_you_mean(search_word[1])}", match)
|
318
|
+
search_type = false
|
319
|
+
search_terms = false
|
320
|
+
end
|
321
|
+
elsif link_text && !link_text.empty? && (!link_info || link_info.empty?)
|
322
|
+
search_type = SL::GoogleSearch.test_for_key ? 'gg' : 'g'
|
323
|
+
search_terms = link_text
|
324
|
+
elsif link_info && !link_info.empty?
|
325
|
+
search_type = SL::GoogleSearch.test_for_key ? 'gg' : 'g'
|
326
|
+
search_terms = link_info
|
327
|
+
else
|
328
|
+
SL.add_error('Invalid search', match)
|
329
|
+
search_type = false
|
330
|
+
search_terms = false
|
331
|
+
end
|
332
|
+
|
333
|
+
if search_type && !search_terms.empty?
|
334
|
+
SL.config['custom_site_searches'].each do |k, v|
|
335
|
+
next unless search_type == k
|
336
|
+
|
337
|
+
link_text = search_terms if !SL.titleize && link_text == ''
|
338
|
+
v = parse_arguments(v, { no_restore: true })
|
339
|
+
if v =~ %r{^(/|http)}i
|
340
|
+
search_type = 'r'
|
341
|
+
tokens = v.scan(/\$term\d+[ds]?/).sort.uniq
|
342
|
+
|
343
|
+
if !tokens.empty?
|
344
|
+
highest_token = 0
|
345
|
+
tokens.each do |token|
|
346
|
+
if token =~ /(\d+)[ds]?$/ && Regexp.last_match(1).to_i > highest_token
|
347
|
+
highest_token = Regexp.last_match(1).to_i
|
348
|
+
end
|
349
|
+
end
|
350
|
+
terms_p = search_terms.split(/ +/)
|
351
|
+
if terms_p.length > highest_token
|
352
|
+
remainder = terms_p[highest_token - 1..-1].join(' ')
|
353
|
+
terms_p = terms_p[0..highest_token - 2]
|
354
|
+
terms_p.push(remainder)
|
355
|
+
end
|
356
|
+
tokens.each do |t|
|
357
|
+
next unless t =~ /(\d+)[ds]?$/
|
358
|
+
|
359
|
+
int = Regexp.last_match(1).to_i - 1
|
360
|
+
replacement = terms_p[int]
|
361
|
+
case t
|
362
|
+
when /d$/
|
363
|
+
replacement.downcase!
|
364
|
+
re_down = ''
|
365
|
+
when /s$/
|
366
|
+
replacement.slugify!
|
367
|
+
re_down = ''
|
368
|
+
else
|
369
|
+
re_down = '(?!d|s)'
|
370
|
+
end
|
371
|
+
v.gsub!(/#{Regexp.escape(t) + re_down}/, replacement.url_encode)
|
372
|
+
end
|
373
|
+
search_terms = v
|
374
|
+
else
|
375
|
+
search_terms = v.gsub(/\$term[ds]?/i) do |mtch|
|
376
|
+
search_terms.downcase! if mtch =~ /d$/i
|
377
|
+
search_terms.slugify! if mtch =~ /s$/i
|
378
|
+
search_terms.url_encode
|
379
|
+
end
|
380
|
+
end
|
381
|
+
else
|
382
|
+
search_type = SL::GoogleSearch.test_for_key ? 'gg' : 'g'
|
383
|
+
search_terms = "site:#{v} #{search_terms}"
|
384
|
+
end
|
385
|
+
|
386
|
+
break
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
if (search_type && search_terms) || url
|
391
|
+
# warn "Searching #{search_type} for #{search_terms}"
|
392
|
+
if (!url)
|
393
|
+
search_count += 1
|
394
|
+
url, title, link_text = do_search(search_type, search_terms, link_text, search_count)
|
395
|
+
end
|
396
|
+
|
397
|
+
if url
|
398
|
+
title = SL::URL.title(url) if SL.titleize && title == ''
|
399
|
+
|
400
|
+
link_text = title if link_text == '' && title
|
401
|
+
force_title = search_type =~ /def/ ? true : false
|
402
|
+
|
403
|
+
if link_only || search_type =~ /sp(ell)?/ || url == 'embed'
|
404
|
+
url = title if url == 'embed'
|
405
|
+
cursor_difference += SL.match_length - url.length
|
406
|
+
SL.match_length = url.length
|
407
|
+
SL.add_report("#{match_string} => #{url}")
|
408
|
+
url
|
409
|
+
elsif ref_title
|
410
|
+
unless links.key? url
|
411
|
+
links[url] = link_text
|
412
|
+
SL.add_footer SL.make_link(:ref_title, link_text, url, title: title, force_title: force_title)
|
413
|
+
end
|
414
|
+
delete_line = true
|
415
|
+
elsif SL.config['inline']
|
416
|
+
res = SL.make_link(:inline, link_text, url, title: title, force_title: force_title)
|
417
|
+
cursor_difference += SL.match_length - res.length
|
418
|
+
SL.match_length = res.length
|
419
|
+
SL.add_report("#{match_string} => #{url}")
|
420
|
+
res
|
421
|
+
else
|
422
|
+
unless links.key? url
|
423
|
+
highest_marker += 1
|
424
|
+
links[url] = format('%<pre>s%<m>04d', pre: prefix, m: highest_marker)
|
425
|
+
SL.add_footer SL.make_link(:ref_title, links[url], url, title: title, force_title: force_title)
|
426
|
+
end
|
427
|
+
|
428
|
+
type = SL.config['inline'] ? :inline : :ref_link
|
429
|
+
res = SL.make_link(type, link_text, links[url], title: false, force_title: force_title)
|
430
|
+
cursor_difference += SL.match_length - res.length
|
431
|
+
SL.match_length = res.length
|
432
|
+
SL.add_report("#{match_string} => #{url}")
|
433
|
+
res
|
434
|
+
end
|
435
|
+
else
|
436
|
+
SL.add_error('No results', "#{search_terms} (#{match_string})")
|
437
|
+
counter_errors += 1
|
438
|
+
match
|
439
|
+
end
|
440
|
+
else
|
441
|
+
SL.add_error('Invalid search', match)
|
442
|
+
counter_errors += 1
|
443
|
+
match
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
line_difference += 1 if delete_line
|
448
|
+
out.push(line) unless delete_line
|
449
|
+
delete_line = false
|
450
|
+
end
|
451
|
+
warn "\n" unless SILENT
|
452
|
+
|
453
|
+
input = out.delete_if { |l| l.strip =~ /^<!--DELETE-->$/ }.join("\n")
|
454
|
+
|
455
|
+
if SL.config['inline']
|
456
|
+
SL.add_output "#{input}\n"
|
457
|
+
SL.add_output "\n#{SL.print_footer}" unless SL.footer.empty?
|
458
|
+
elsif SL.footer.empty?
|
459
|
+
SL.add_output input
|
460
|
+
else
|
461
|
+
last_line = input.strip.split(/\n/)[-1]
|
462
|
+
case last_line
|
463
|
+
when /^\[.*?\]: http/
|
464
|
+
SL.add_output "#{input.rstrip}\n"
|
465
|
+
when /^\[\^.*?\]: /
|
466
|
+
SL.add_output input.rstrip
|
467
|
+
else
|
468
|
+
SL.add_output "#{input}\n\n"
|
469
|
+
end
|
470
|
+
SL.add_output "#{SL.print_footer}\n\n"
|
471
|
+
end
|
472
|
+
|
473
|
+
SL.line_num = nil
|
474
|
+
SL.add_report("Processed: #{total_links} links, #{counter_errors} errors.")
|
475
|
+
SL.print_report
|
476
|
+
SL.print_errors
|
477
|
+
else
|
478
|
+
link_only = false
|
479
|
+
SL.clipboard = false
|
480
|
+
|
481
|
+
res = parse_arguments(input.strip!).strip
|
482
|
+
input = res.nil? ? input.strip : res
|
483
|
+
|
484
|
+
# if the end of input contain "^", copy to clipboard instead of STDOUT
|
485
|
+
SL.clipboard = true if input =~ /\^[!~:\s]*$/
|
486
|
+
|
487
|
+
# if the end of input contains "!!", only print the url
|
488
|
+
link_only = true if input =~ /!![\^~:\s]*$/
|
489
|
+
|
490
|
+
reference_link = input =~ /:([!\^~\s]*)$/
|
491
|
+
|
492
|
+
# if end of input contains ~, pull url from clipboard
|
493
|
+
if input =~ /~[:\^!\s]*$/
|
494
|
+
input.sub!(/[:!\^\s~]*$/, '')
|
495
|
+
clipboard = `__CF_USER_TEXT_ENCODING=$UID:0x8000100:0x8000100 pbpaste`.strip
|
496
|
+
if SL::URL.url?(clipboard)
|
497
|
+
type = reference_link ? :ref_title : :inline
|
498
|
+
print SL.make_link(type, input.strip, clipboard)
|
499
|
+
else
|
500
|
+
print SL.originput
|
501
|
+
end
|
502
|
+
Process.exit
|
503
|
+
end
|
504
|
+
|
505
|
+
input.sub!(/[:!\^\s~]*$/, '')
|
506
|
+
|
507
|
+
## Maybe if input is just a URL, convert it to a link
|
508
|
+
## using hostname as text without doing search
|
509
|
+
if SL::URL.only_url?(input.strip)
|
510
|
+
type = reference_link ? :ref_title : :inline
|
511
|
+
url, title = SL::URL.url_to_link(input.strip, type)
|
512
|
+
print SL.make_link(type, title, url, title: false, force_title: false)
|
513
|
+
Process.exit
|
514
|
+
end
|
515
|
+
|
516
|
+
# check for additional search terms in parenthesis
|
517
|
+
additional_terms = ''
|
518
|
+
if input =~ /\((.*?)\)/
|
519
|
+
additional_terms = " #{Regexp.last_match(1).strip}"
|
520
|
+
input.sub!(/\(.*?\)/, '')
|
521
|
+
end
|
522
|
+
|
523
|
+
# Maybe detect "search + addition terms" and remove additional terms from link text?
|
524
|
+
# if input =~ /\+(.+?)$/
|
525
|
+
# additional_terms = "#{additional_terms} #{Regexp.last_match(1).strip}"
|
526
|
+
# input.sub!(/\+.*?$/, '').strip!
|
527
|
+
# end
|
528
|
+
|
529
|
+
link_text = false
|
530
|
+
|
531
|
+
if input =~ /"(.*?)"/
|
532
|
+
link_text = Regexp.last_match(1)
|
533
|
+
input.gsub!(/"(.*?)"/, '\1')
|
534
|
+
end
|
535
|
+
|
536
|
+
# remove quotes from terms, just in case
|
537
|
+
# input.sub!(/^(!\S+)?\s*(["'])(.*?)\2([\!\^]+)?$/, "\\1 \\3\\4")
|
538
|
+
|
539
|
+
case input
|
540
|
+
when /^!(\S+)\s+(.*)$/
|
541
|
+
type = Regexp.last_match(1)
|
542
|
+
link_info = Regexp.last_match(2).strip
|
543
|
+
link_text ||= link_info
|
544
|
+
terms = link_info + additional_terms
|
545
|
+
terms.strip!
|
546
|
+
|
547
|
+
if SL::Searches.valid_search?(type) || type =~ /^(\S+\.)+\S+$/
|
548
|
+
if type && terms && !terms.empty?
|
549
|
+
# Iterate through custom searches for a match, perform search if matched
|
550
|
+
SL.config['custom_site_searches'].each do |k, v|
|
551
|
+
next unless type == k
|
552
|
+
|
553
|
+
link_text = terms if link_text == ''
|
554
|
+
v = parse_arguments(v, { no_restore: true })
|
555
|
+
if v =~ %r{^(/|http)}i
|
556
|
+
type = 'r'
|
557
|
+
tokens = v.scan(/\$term\d+[ds]?/).sort.uniq
|
558
|
+
|
559
|
+
if !tokens.empty?
|
560
|
+
highest_token = 0
|
561
|
+
tokens.each do |token|
|
562
|
+
t = Regexp.last_match(1)
|
563
|
+
highest_token = t.to_i if token =~ /(\d+)d?$/ && t.to_i > highest_token
|
564
|
+
end
|
565
|
+
terms_p = terms.split(/ +/)
|
566
|
+
if terms_p.length > highest_token
|
567
|
+
remainder = terms_p[highest_token - 1..].join(' ')
|
568
|
+
terms_p = terms_p[0..highest_token - 2]
|
569
|
+
terms_p.push(remainder)
|
570
|
+
end
|
571
|
+
tokens.each do |t|
|
572
|
+
next unless t =~ /(\d+)d?$/
|
573
|
+
|
574
|
+
int = Regexp.last_match(1).to_i - 1
|
575
|
+
replacement = terms_p[int]
|
576
|
+
|
577
|
+
re_down = case t
|
578
|
+
when /d$/
|
579
|
+
replacement.downcase!
|
580
|
+
''
|
581
|
+
when /s$/
|
582
|
+
replacement.slugify!
|
583
|
+
''
|
584
|
+
else
|
585
|
+
'(?!d|s)'
|
586
|
+
end
|
587
|
+
v.gsub!(/#{Regexp.escape(t) + re_down}/, replacement.url_encode)
|
588
|
+
end
|
589
|
+
terms = v
|
590
|
+
else
|
591
|
+
terms = v.gsub(/\$term[ds]?/i) do |mtch|
|
592
|
+
terms.downcase! if mtch =~ /d$/i
|
593
|
+
terms.slugify! if mtch =~ /s$/i
|
594
|
+
terms.url_encode
|
595
|
+
end
|
596
|
+
end
|
597
|
+
else
|
598
|
+
type = SL::GoogleSearch.test_for_key ? 'gg' : 'g'
|
599
|
+
terms = "site:#{v} #{terms}"
|
600
|
+
end
|
601
|
+
|
602
|
+
break
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
# if contains TLD, use site-specific search
|
607
|
+
if type =~ /^(\S+\.)+\S+$/
|
608
|
+
terms = "site:#{type} #{terms}"
|
609
|
+
type = SL::GoogleSearch.test_for_key ? 'gg' : 'g'
|
610
|
+
end
|
611
|
+
search_count ||= 0
|
612
|
+
search_count += 1
|
613
|
+
|
614
|
+
url, title, link_text = do_search(type, terms, link_text, search_count)
|
615
|
+
else
|
616
|
+
SL.add_error("Invalid search#{SL::Searches.did_you_mean(type)}", input)
|
617
|
+
counter_errors += 1
|
618
|
+
end
|
619
|
+
# Social handle expansion
|
620
|
+
when /^([tfilm])?@(\S+)\s*$/
|
621
|
+
type = Regexp.last_match(1)
|
622
|
+
unless type
|
623
|
+
# If contains @ mid-handle, use Mastodon
|
624
|
+
if Regexp.last_match(2) =~ /[a-z0-9_]@[a-z0-9_.]+/i
|
625
|
+
type = 'm'
|
626
|
+
else
|
627
|
+
type = 't'
|
628
|
+
end
|
629
|
+
end
|
630
|
+
link_text = input.sub(/^[tfilm]/, '')
|
631
|
+
url, title = SL::SocialSearch.social_handle(type, link_text)
|
632
|
+
link_text = title
|
633
|
+
else
|
634
|
+
link_text ||= input
|
635
|
+
url, title, link_text = SL.ddg(input, link_text)
|
636
|
+
end
|
637
|
+
|
638
|
+
if url
|
639
|
+
if type =~ /sp(ell)?/
|
640
|
+
SL.add_output(url)
|
641
|
+
elsif link_only
|
642
|
+
SL.add_output(url)
|
643
|
+
elsif url == 'embed'
|
644
|
+
SL.add_output(title)
|
645
|
+
else
|
646
|
+
type = reference_link ? :ref_title : :inline
|
647
|
+
|
648
|
+
SL.add_output SL.make_link(type, link_text, url, title: title, force_title: false)
|
649
|
+
SL.print_errors
|
650
|
+
end
|
651
|
+
else
|
652
|
+
SL.add_error('No results', title)
|
653
|
+
SL.add_output SL.originput.chomp
|
654
|
+
SL.print_errors
|
655
|
+
end
|
656
|
+
|
657
|
+
if SL.clipboard
|
658
|
+
if SL.output == SL.originput
|
659
|
+
warn 'No results found'
|
660
|
+
else
|
661
|
+
`echo #{Shellwords.escape(SL.output.join(''))}|tr -d "\n"|pbcopy`
|
662
|
+
warn 'Results in clipboard'
|
663
|
+
end
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|