searchlink 2.3.84 → 2.3.86
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/config.rb +9 -1
- data/lib/searchlink/curl/html.rb +17 -9
- data/lib/searchlink/output.rb +32 -10
- data/lib/searchlink/parse.rb +252 -261
- data/lib/searchlink/searches/helpers/safari.rb +6 -1
- data/lib/searchlink/searches/history.rb +5 -6
- data/lib/searchlink/searches/popup.rb +68 -0
- data/lib/searchlink/searches/shortener.rb +81 -0
- data/lib/searchlink/searches/{bitly.rb → shorteners/bitly.rb} +0 -1
- data/lib/searchlink/searches/shorteners/tinyurl.rb +72 -0
- data/lib/searchlink/searches.rb +27 -25
- data/lib/searchlink/string.rb +105 -36
- data/lib/searchlink/url.rb +2 -0
- data/lib/searchlink/util.rb +4 -3
- data/lib/searchlink/version.rb +1 -1
- metadata +9 -6
- /data/lib/searchlink/searches/{isgd.rb → shorteners/isgd.rb} +0 -0
data/lib/searchlink/parse.rb
CHANGED
@@ -2,194 +2,14 @@
|
|
2
2
|
|
3
3
|
module SL
|
4
4
|
class SearchLink
|
5
|
-
#
|
6
|
-
|
7
|
-
|
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
|
5
|
+
# Confirm a URL with a popup if requested
|
6
|
+
def confirmed?(url)
|
7
|
+
return true unless SL.config["confirm"]
|
125
8
|
|
126
|
-
|
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]
|
9
|
+
SL::Shortener.confirm?(url)
|
191
10
|
end
|
192
11
|
|
12
|
+
# Parse the input string and perform searches
|
193
13
|
def parse(input)
|
194
14
|
SL.output = []
|
195
15
|
return false if input.empty?
|
@@ -202,6 +22,7 @@ module SL
|
|
202
22
|
SL.config["inline"] = true if input.scan(/\]\(/).length == 1 && input.split(/\n/).length == 1
|
203
23
|
SL.errors = {}
|
204
24
|
SL.report = []
|
25
|
+
SL.shortener = :none
|
205
26
|
|
206
27
|
# Check for new version
|
207
28
|
latest_version = SL.new_version?
|
@@ -274,6 +95,7 @@ module SL
|
|
274
95
|
end
|
275
96
|
end
|
276
97
|
|
98
|
+
# Handle links in the form of [text](url) or [text](url "title")
|
277
99
|
if input =~ /\[(.*?)\]\((.*?)\)/
|
278
100
|
lines = input.split(/\n/)
|
279
101
|
out = []
|
@@ -330,6 +152,7 @@ module SL
|
|
330
152
|
|
331
153
|
@link_text = this_match[1] || ""
|
332
154
|
link_info = parse_arguments(this_match[2].strip).strip || ""
|
155
|
+
query, link_info = link_info.extract_query({})
|
333
156
|
|
334
157
|
if @link_text.strip == "" && link_info =~ /".*?"/
|
335
158
|
link_info.gsub!(/"(.*?)"/) do
|
@@ -355,7 +178,7 @@ module SL
|
|
355
178
|
end
|
356
179
|
|
357
180
|
if link_info =~ /^!(\S+)/
|
358
|
-
search_type = Regexp.last_match(1)
|
181
|
+
search_type = Regexp.last_match(1).extract_shortener
|
359
182
|
unless SL::Searches.valid_search?(search_type) || search_type =~ /^(\S+\.)+\S+$/
|
360
183
|
SL.add_error("Invalid search#{SL::Searches.did_you_mean(search_type)}", match)
|
361
184
|
invalid_search = true
|
@@ -386,10 +209,12 @@ module SL
|
|
386
209
|
m = Regexp.last_match
|
387
210
|
|
388
211
|
search_type = if m[1].nil?
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
212
|
+
SL::GoogleSearch.api_key? ? "gg" : "g"
|
213
|
+
else
|
214
|
+
m[1]
|
215
|
+
end
|
216
|
+
|
217
|
+
search_type.extract_shortener!
|
393
218
|
|
394
219
|
search_terms = m[2].gsub(/(^["']|["']$)/, "")
|
395
220
|
search_terms.strip!
|
@@ -419,18 +244,17 @@ module SL
|
|
419
244
|
search_type = "g"
|
420
245
|
search_terms = "site:#{m[1]} #{search_terms}"
|
421
246
|
end
|
422
|
-
|
423
247
|
elsif link_info =~ /^!/
|
424
248
|
search_word = link_info.match(/^!(\S+)/)
|
425
|
-
|
426
|
-
if search_word && SL::Searches.valid_search?(
|
427
|
-
search_type =
|
249
|
+
st = search_word[1].extract_shortener
|
250
|
+
if search_word && SL::Searches.valid_search?(st)
|
251
|
+
search_type = st unless search_word.nil?
|
428
252
|
search_terms = @link_text
|
429
|
-
elsif search_word &&
|
253
|
+
elsif search_word && st =~ /^(\S+\.)+\S+$/
|
430
254
|
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
431
255
|
search_terms = "site:#{search_word[1]} #{@link_text}"
|
432
256
|
else
|
433
|
-
SL.add_error("Invalid search#{SL::Searches.did_you_mean(
|
257
|
+
SL.add_error("Invalid search#{SL::Searches.did_you_mean(st)}", match)
|
434
258
|
search_type = false
|
435
259
|
search_terms = false
|
436
260
|
end
|
@@ -450,6 +274,8 @@ module SL
|
|
450
274
|
search_type, search_terms = custom_search(search_type, search_terms)
|
451
275
|
end
|
452
276
|
|
277
|
+
SL.add_query(query) if query
|
278
|
+
|
453
279
|
if (search_type && search_terms) || @url
|
454
280
|
# warn "Searching #{search_type} for #{search_terms}"
|
455
281
|
|
@@ -465,6 +291,13 @@ module SL
|
|
465
291
|
end
|
466
292
|
|
467
293
|
if @url
|
294
|
+
res = confirmed?(@url)
|
295
|
+
if !res
|
296
|
+
return match
|
297
|
+
else
|
298
|
+
@url = res if res.is_a?(String) && SL::URL.url?(res)
|
299
|
+
end
|
300
|
+
|
468
301
|
title = SL::URL.title(@url) if SL.titleize && title == ""
|
469
302
|
|
470
303
|
@link_text = title if @link_text == "" && title
|
@@ -544,12 +377,13 @@ module SL
|
|
544
377
|
SL.add_report("Processed: #{total_links} links, #{counter_errors} errors.")
|
545
378
|
SL.print_report
|
546
379
|
SL.print_errors
|
547
|
-
else
|
380
|
+
else # Assume single line input
|
548
381
|
link_only = false
|
549
382
|
SL.clipboard = false
|
550
383
|
|
551
384
|
res = parse_arguments(input.strip!).strip
|
552
385
|
input = res.nil? ? input.strip : res
|
386
|
+
query, input = input.extract_query({})
|
553
387
|
|
554
388
|
# if the end of input contain "^", copy to clipboard instead of STDOUT
|
555
389
|
SL.clipboard = true if input =~ /\^[!~:\s]*$/
|
@@ -608,8 +442,9 @@ module SL
|
|
608
442
|
|
609
443
|
case input
|
610
444
|
when /^!(\S+)\s+(.*)$/
|
611
|
-
type = Regexp.last_match(1)
|
445
|
+
type = Regexp.last_match(1).extract_shortener
|
612
446
|
link_info = Regexp.last_match(2).strip
|
447
|
+
|
613
448
|
@link_text ||= link_info
|
614
449
|
terms = link_info + additional_terms
|
615
450
|
terms.strip!
|
@@ -617,60 +452,7 @@ module SL
|
|
617
452
|
if SL::Searches.valid_search?(type) || type =~ /^(\S+\.)+\S+$/
|
618
453
|
if type && terms && !terms.empty?
|
619
454
|
# 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
|
455
|
+
type, terms = custom_search(type, terms)
|
674
456
|
end
|
675
457
|
|
676
458
|
# if contains TLD, use site-specific search
|
@@ -681,6 +463,7 @@ module SL
|
|
681
463
|
@search_count ||= 0
|
682
464
|
@search_count += 1
|
683
465
|
|
466
|
+
SL.add_query(query) if query
|
684
467
|
@url, title, @link_text = do_search(type, terms, @link_text, @search_count)
|
685
468
|
else
|
686
469
|
SL.add_error("Invalid search#{SL::Searches.did_you_mean(type)}", input)
|
@@ -695,24 +478,38 @@ module SL
|
|
695
478
|
"t"
|
696
479
|
end
|
697
480
|
@link_text = input.sub(/^[tfilm]/, "")
|
481
|
+
SL.add_query(query) if query
|
698
482
|
@url, title = SL::SocialSearch.social_handle(type, @link_text)
|
699
483
|
@link_text = title
|
700
484
|
else
|
485
|
+
SL.add_query(query) if query
|
701
486
|
@link_text ||= input
|
702
487
|
@url, title, @link_text = SL.ddg(input, @link_text)
|
703
488
|
end
|
704
489
|
|
705
490
|
if @url
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
else
|
713
|
-
type = reference_link ? :ref_title : :inline
|
491
|
+
res = confirmed?(@url)
|
492
|
+
if res
|
493
|
+
if res.is_a?(String) && SL::URL.url?(res)
|
494
|
+
@url = res
|
495
|
+
title = SL::URL.title(@url) unless title == ""
|
496
|
+
end
|
714
497
|
|
715
|
-
|
498
|
+
if type =~ /sp(ell)?/
|
499
|
+
SL.add_output(@url)
|
500
|
+
elsif link_only
|
501
|
+
SL.add_output(@url)
|
502
|
+
elsif @url == "embed"
|
503
|
+
SL.add_output(title)
|
504
|
+
else
|
505
|
+
type = reference_link ? :ref_title : :inline
|
506
|
+
|
507
|
+
SL.add_output SL.make_link(type, @link_text, @url, title: title, force_title: false)
|
508
|
+
SL.print_errors
|
509
|
+
end
|
510
|
+
else
|
511
|
+
SL.add_error("Canceled", "User canceled result #{@url}")
|
512
|
+
SL.add_output SL.originput.chomp
|
716
513
|
SL.print_errors
|
717
514
|
end
|
718
515
|
else
|
@@ -731,5 +528,199 @@ module SL
|
|
731
528
|
end
|
732
529
|
end
|
733
530
|
end
|
531
|
+
|
532
|
+
private
|
533
|
+
|
534
|
+
def add_title(link_info)
|
535
|
+
@url = link_info
|
536
|
+
title = SL::URL.title(@url)
|
537
|
+
@link_text = title
|
538
|
+
|
539
|
+
if @ref_title
|
540
|
+
unless @links.key? @url
|
541
|
+
@links[@url] = @link_text
|
542
|
+
SL.add_footer SL.make_link(:ref_title, @link_text, @url, title: title, force_title: false)
|
543
|
+
end
|
544
|
+
@delete_line = true
|
545
|
+
elsif SL.config["inline"]
|
546
|
+
res = SL.make_link(:inline, @link_text, @url, title: title, force_title: false)
|
547
|
+
@cursor_difference += SL.match_length - res.length
|
548
|
+
SL.match_length = res.length
|
549
|
+
SL.add_report("#{@match_string} => #{@url}")
|
550
|
+
res
|
551
|
+
else
|
552
|
+
unless @links.key? @url
|
553
|
+
@highest_marker += 1
|
554
|
+
@links[@url] = format("%<pre>s%<m>04d", pre: @prefix, m: @highest_marker)
|
555
|
+
SL.add_footer SL.make_link(:ref_title, @links[@url], @url, title: title, force_title: false)
|
556
|
+
end
|
557
|
+
|
558
|
+
type = SL.config["inline"] ? :inline : :ref_link
|
559
|
+
res = SL.make_link(type, @link_text, @links[@url], title: false, force_title: false)
|
560
|
+
@cursor_difference += SL.match_length - res.length
|
561
|
+
SL.match_length = res.length
|
562
|
+
SL.add_report("#{@match_string} => #{@url}")
|
563
|
+
res
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
# Parse arguments in the input string
|
568
|
+
#
|
569
|
+
# @param string [String] the string to parse
|
570
|
+
# @param opt [Hash] the options to parse
|
571
|
+
# @option opt [Boolean] :only_meta (false) whether to skip flags
|
572
|
+
# @option opt [Boolean] :no_restore (false) whether to restore previous config
|
573
|
+
# @return [String] the parsed string
|
574
|
+
#
|
575
|
+
def parse_arguments(string, opt = {})
|
576
|
+
input = string.dup
|
577
|
+
return "" if input.nil?
|
578
|
+
|
579
|
+
skip_flags = opt[:only_meta] || false
|
580
|
+
no_restore = opt[:no_restore] || false
|
581
|
+
restore_prev_config unless no_restore
|
582
|
+
|
583
|
+
input.parse_flags! unless skip_flags
|
584
|
+
|
585
|
+
options = %w[debug country_code inline prefix_random include_titles remove_seo validate_links complete_bare confirm]
|
586
|
+
options.each do |o|
|
587
|
+
if input =~ /^ *#{o}:\s+(\S+)$/
|
588
|
+
val = Regexp.last_match(1).strip
|
589
|
+
|
590
|
+
if val.is_a?(String)
|
591
|
+
value = true if val =~ /true/i
|
592
|
+
value = false if val =~ /false/i
|
593
|
+
end
|
594
|
+
val = value if value
|
595
|
+
SL.config[o] = val
|
596
|
+
warn "\r\033[0KGlobal config: #{o} = #{SL.config[o]}\n" unless SILENT
|
597
|
+
end
|
598
|
+
|
599
|
+
next if skip_flags
|
600
|
+
|
601
|
+
while input =~ /^#{o}:\s+(.*?)$/ || input =~ /--(no-)?#{o}/
|
602
|
+
next unless input =~ /--(no-)?#{o}/ && !skip_flags
|
603
|
+
|
604
|
+
unless SL.prev_config.key? o
|
605
|
+
SL.prev_config[o] = SL.config[o]
|
606
|
+
bool = Regexp.last_match(1).nil? || Regexp.last_match(1) == "" ? true : false
|
607
|
+
SL.config[o] = bool
|
608
|
+
$stderr.print "\r\033[0KLine config: #{o} = #{SL.config[o]}\n" unless SILENT
|
609
|
+
end
|
610
|
+
input.sub!(/\s?--(no-)?#{o}/, "")
|
611
|
+
end
|
612
|
+
end
|
613
|
+
SL.clipboard ? string : input
|
614
|
+
end
|
615
|
+
|
616
|
+
# Parse commands from the given input string
|
617
|
+
#
|
618
|
+
# @param input [String] the input string
|
619
|
+
def parse_commands(input)
|
620
|
+
# Handle commands like help or docs
|
621
|
+
return unless input.strip =~ /^!?(h(elp)?|wiki|docs?|v(er(s(ion)?)?)?|up(date|grade))$/
|
622
|
+
|
623
|
+
case input.strip
|
624
|
+
when /^!?help$/i
|
625
|
+
if SILENT
|
626
|
+
help_dialog
|
627
|
+
else
|
628
|
+
$stdout.puts SL.version_check.to_s
|
629
|
+
$stdout.puts "See https://github.com/ttscoff/searchlink/wiki for help"
|
630
|
+
end
|
631
|
+
print input
|
632
|
+
when /^!?(wiki|docs)$/i
|
633
|
+
warn "Opening wiki in browser"
|
634
|
+
`open https://github.com/ttscoff/searchlink/wiki`
|
635
|
+
when /^!?v(er(s(ion)?)?)?$/
|
636
|
+
print "[#{SL.version_check}]"
|
637
|
+
when /^!?up(date|grade)$/
|
638
|
+
SL.update_searchlink
|
639
|
+
print SL.output.join("")
|
640
|
+
end
|
641
|
+
Process.exit 0
|
642
|
+
end
|
643
|
+
|
644
|
+
def create_footnote(mtch)
|
645
|
+
if mtch[1].nil? || mtch[1] == ""
|
646
|
+
match
|
647
|
+
else
|
648
|
+
note = mtch[1].strip
|
649
|
+
@footnote_counter += 1
|
650
|
+
ref = if !@link_text.empty? && @link_text.scan(/\s/).empty?
|
651
|
+
@link_text
|
652
|
+
else
|
653
|
+
format("%<p>sfn%<c>04d", p: @prefix, c: @footnote_counter)
|
654
|
+
end
|
655
|
+
SL.add_footer "[^#{ref}]: #{note}"
|
656
|
+
res = "[^#{ref}]"
|
657
|
+
@cursor_difference += (SL.match_length - res.length)
|
658
|
+
SL.match_length = res.length
|
659
|
+
SL.add_report("#{@match_string} => Footnote #{ref}")
|
660
|
+
res
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
def custom_search(search_type, search_terms)
|
665
|
+
SL.config["custom_site_searches"].each do |k, v|
|
666
|
+
next unless search_type == k
|
667
|
+
|
668
|
+
@link_text = search_terms if !SL.titleize && @link_text == ""
|
669
|
+
v = parse_arguments(v, { no_restore: true })
|
670
|
+
query, v = v.extract_query({})
|
671
|
+
|
672
|
+
SL.add_query(query)
|
673
|
+
|
674
|
+
if v =~ %r{^(/|http)}i
|
675
|
+
search_type = "r"
|
676
|
+
tokens = v.scan(/\$term\d+[ds]?/).sort.uniq
|
677
|
+
|
678
|
+
if !tokens.empty?
|
679
|
+
highest_token = 0
|
680
|
+
tokens.each do |token|
|
681
|
+
if token =~ /(\d+)[ds]?$/ && Regexp.last_match(1).to_i > highest_token
|
682
|
+
highest_token = Regexp.last_match(1).to_i
|
683
|
+
end
|
684
|
+
end
|
685
|
+
terms_p = search_terms.split(/ +/)
|
686
|
+
if terms_p.length > highest_token
|
687
|
+
remainder = terms_p[highest_token - 1..].join(" ")
|
688
|
+
terms_p = terms_p[0..highest_token - 2]
|
689
|
+
terms_p.push(remainder)
|
690
|
+
end
|
691
|
+
tokens.each do |t|
|
692
|
+
next unless t =~ /(\d+)[ds]?$/
|
693
|
+
|
694
|
+
int = Regexp.last_match(1).to_i - 1
|
695
|
+
replacement = terms_p[int]
|
696
|
+
case t
|
697
|
+
when /d$/
|
698
|
+
replacement.downcase!
|
699
|
+
re_down = ""
|
700
|
+
when /s$/
|
701
|
+
replacement.slugify!
|
702
|
+
re_down = ""
|
703
|
+
else
|
704
|
+
re_down = "(?!d|s)"
|
705
|
+
end
|
706
|
+
v.gsub!(/#{Regexp.escape(t) + re_down}/, replacement.url_encode)
|
707
|
+
end
|
708
|
+
search_terms = v
|
709
|
+
else
|
710
|
+
search_terms = v.gsub(/\$term[ds]?/i) do |mtch|
|
711
|
+
search_terms.downcase! if mtch =~ /d$/i
|
712
|
+
search_terms.slugify! if mtch =~ /s$/i
|
713
|
+
search_terms.url_encode
|
714
|
+
end
|
715
|
+
end
|
716
|
+
else
|
717
|
+
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
718
|
+
search_terms = "site:#{v} #{search_terms}"
|
719
|
+
end
|
720
|
+
|
721
|
+
break
|
722
|
+
end
|
723
|
+
[search_type, search_terms]
|
724
|
+
end
|
734
725
|
end
|
735
726
|
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[
|