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.
@@ -2,194 +2,14 @@
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
5
+ # Confirm a URL with a popup if requested
6
+ def confirmed?(url)
7
+ return true unless SL.config["confirm"]
125
8
 
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]
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
- SL::GoogleSearch.api_key? ? "gg" : "g"
390
- else
391
- m[1]
392
- end
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?(search_word[1])
427
- search_type = search_word[1] unless search_word.nil?
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 && search_word[1] =~ /^(\S+\.)+\S+$/
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(search_word[1])}", match)
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
- SL.config["custom_site_searches"].each do |k, v|
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
- if type =~ /sp(ell)?/
707
- SL.add_output(@url)
708
- elsif link_only
709
- SL.add_output(@url)
710
- elsif @url == "embed"
711
- SL.add_output(title)
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
- SL.add_output SL.make_link(type, @link_text, @url, title: title, force_title: false)
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 -convert xml1 -o - ~/Library/Safari/Bookmarks.plist`.strip
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
- ["Remove or comment (with #) history searches you don't want",
50
- "performed by `!h`. You can force-enable them per search, e.g.",
51
- "`!hsh` (Safari History only), `!hcb` (Chrome Bookmarks only)",
52
- "etc. Multiple types can be strung together: !hshcb (Safari",
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[