searchlink 2.3.87 → 2.3.88
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/curl/html.rb +1 -1
- data/lib/searchlink/output.rb +20 -8
- data/lib/searchlink/parse.rb +403 -372
- data/lib/searchlink/searches/duckduckgo.rb +2 -1
- data/lib/searchlink/searches/google.rb +1 -1
- data/lib/searchlink/string.rb +26 -0
- data/lib/searchlink/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d1e0567872f0b80b0d51b37a4522c91cd70f3af268b07ee9d2ed35e56ee3288
|
4
|
+
data.tar.gz: a5327a1f2e2d355b3f1ba7c5818f9b51c640207e3e0e48374527038e4e2d049e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6bbf35b751bbdfc03e990ef90de99b62117183759640c0ffc102d606be0aed4e8982bec6d1bfaec7ffc67e0dd4670252174307ef2d8953752402f6789e388953
|
7
|
+
data.tar.gz: 1f2e05c2e4220b8f38a6bebe99c1ca019474d8dde50e2c61ecf57e557c265ad2536d317a5d7e47ad5583dc065ae983fa475f215650e1ceeb24c24b1e07440618
|
data/lib/searchlink/curl/html.rb
CHANGED
data/lib/searchlink/output.rb
CHANGED
@@ -3,14 +3,19 @@
|
|
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, :
|
7
|
-
:shortener
|
6
|
+
:match_column, :match_length, :originput, :errors, :error_count,
|
7
|
+
:report, :printout, :shortener
|
8
8
|
|
9
9
|
# Whether or not to add a title to the output
|
10
10
|
def titleize
|
11
11
|
@titleize ||= false
|
12
12
|
end
|
13
13
|
|
14
|
+
# Count of errors
|
15
|
+
def error_count
|
16
|
+
@error_count ||= 0
|
17
|
+
end
|
18
|
+
|
14
19
|
# Whether or not to copy results to clipbpard
|
15
20
|
def clipboard
|
16
21
|
@clipboard ||= false
|
@@ -106,11 +111,17 @@ module SL
|
|
106
111
|
# @return [String] The link.
|
107
112
|
#
|
108
113
|
def make_link(type, text, url, title: false, force_title: false)
|
109
|
-
|
110
|
-
|
111
|
-
|
114
|
+
url.sub!(%r{^//}, "https://")
|
115
|
+
is_image = url =~ /(gif|jpe?g|png|webp)(?:\?.*?)?$/ ? true : false
|
116
|
+
if is_image
|
117
|
+
title = text || File.basename(url).sub(/\?.*$/, "")
|
118
|
+
text = title if text.nil? || text.strip.empty?
|
119
|
+
else
|
120
|
+
title = title.gsub(/\P{Print}|\p{Cf}/, "") if title
|
121
|
+
text = title || SL::URL.title(url) if SL.titleize && (!text || text.strip.empty?)
|
122
|
+
text = text ? text.strip : title
|
123
|
+
end
|
112
124
|
title = title && (SL.config["include_titles"] || force_title) ? %( "#{title.clean}") : ""
|
113
|
-
|
114
125
|
title = title.gsub(/[ \t]+/, " ")
|
115
126
|
|
116
127
|
url.add_query_string!
|
@@ -123,8 +134,7 @@ module SL
|
|
123
134
|
when :ref_link
|
124
135
|
%([#{text}][#{url}])
|
125
136
|
when :inline
|
126
|
-
|
127
|
-
%(#{image}[#{text}](#{url}#{title}))
|
137
|
+
%(#{is_image ? '!' : ''}[#{text}](#{url}#{title}))
|
128
138
|
end
|
129
139
|
end
|
130
140
|
|
@@ -214,6 +224,8 @@ module SL
|
|
214
224
|
end
|
215
225
|
SL.errors[type] ||= []
|
216
226
|
SL.errors[type].push("(#{position}): #{str}")
|
227
|
+
SL.error_count ||= 0
|
228
|
+
SL.error_count += 1
|
217
229
|
end
|
218
230
|
|
219
231
|
# Add to query string
|
data/lib/searchlink/parse.rb
CHANGED
@@ -28,25 +28,14 @@ module SL
|
|
28
28
|
latest_version = SL.new_version?
|
29
29
|
SL.add_output("<!-- v#{latest_version} available, run SearchLink on the word 'update' to install. -->") if latest_version
|
30
30
|
|
31
|
-
@links = {}
|
32
31
|
SL.footer = []
|
33
|
-
counter_links = 0
|
34
|
-
counter_errors = 0
|
35
32
|
|
36
33
|
input.sub!(/\n?<!-- Report:.*?-->\n?/m, "")
|
37
34
|
input.sub!(/\n?<!-- Errors:.*?-->\n?/m, "")
|
38
35
|
|
39
|
-
|
36
|
+
@links = input.scan_links
|
40
37
|
|
41
|
-
@prefix =
|
42
|
-
if input =~ /\[(\d{4}-)\d+\]: \S+/
|
43
|
-
Regexp.last_match(1)
|
44
|
-
else
|
45
|
-
format("%04d-", rand(9999))
|
46
|
-
end
|
47
|
-
else
|
48
|
-
""
|
49
|
-
end
|
38
|
+
@prefix = determine_prefix(input)
|
50
39
|
|
51
40
|
@highest_marker = 0
|
52
41
|
input.scan(/^\s{,3}\[(?:#{@prefix})?(\d+)\]: /).each do
|
@@ -61,16 +50,19 @@ module SL
|
|
61
50
|
end
|
62
51
|
|
63
52
|
if SL.config["complete_bare"]
|
53
|
+
# match all URLs not preceded by a ( or : or <, and are followed by a space or newline
|
64
54
|
rx = %r{(?ix-m)(?<!\(|:\s|<)(?:
|
65
55
|
(?:https?://)(?:[\da-z.-]+)\.(?:[a-z.]{2,6})
|
66
56
|
(?:[/\w\d.\-()_+=?&%]*?(?=[\s\n]|$))
|
67
57
|
)}
|
58
|
+
# replace with [%](url)
|
68
59
|
input.gsub!(rx) do
|
69
60
|
url_match = Regexp.last_match
|
70
61
|
url_match.pre_match =~ /!\S+ +$/ ? url_match[0] : "[%](#{url_match[0]})"
|
71
62
|
end
|
72
63
|
end
|
73
64
|
|
65
|
+
# Handle multi-line links in the form of [\ntext\n](url)
|
74
66
|
if input =~ /\[\n(.*?\n)+\]\((.*?)?\)/
|
75
67
|
input.gsub!(/\[\n(((\s*(?:[-+*]|\d+\.)?\s+)*(!\S+ +)?(.*?))\n)+\]\((!\S+.*?)?\)/) do
|
76
68
|
m = Regexp.last_match
|
@@ -95,431 +87,470 @@ module SL
|
|
95
87
|
|
96
88
|
# Handle links in the form of [text](url) or [text](url "title")
|
97
89
|
if input =~ /\[(.*?)\]\((.*?)\)/
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
line_difference = 0
|
104
|
-
lines.each_with_index do |line, num|
|
105
|
-
SL.line_num = num - line_difference
|
106
|
-
@cursor_difference = 0
|
107
|
-
# ignore links in code blocks
|
108
|
-
if line =~ /^(( {4,}|\t+)[^*+-])/
|
109
|
-
out.push(line)
|
110
|
-
next
|
111
|
-
end
|
112
|
-
if line =~ /^\s*[~`]{3,}/
|
113
|
-
if in_code_block
|
114
|
-
in_code_block = false
|
115
|
-
out.push(line)
|
116
|
-
next
|
117
|
-
else
|
118
|
-
in_code_block = true
|
119
|
-
end
|
120
|
-
end
|
121
|
-
if in_code_block
|
122
|
-
out.push(line)
|
123
|
-
next
|
124
|
-
end
|
90
|
+
parse_brackets(input)
|
91
|
+
else # Assume single line input
|
92
|
+
parse_single_line(input)
|
93
|
+
end
|
94
|
+
end
|
125
95
|
|
126
|
-
|
96
|
+
private
|
127
97
|
|
128
|
-
|
98
|
+
# Determine the prefix for footnotes
|
99
|
+
def determine_prefix(input)
|
100
|
+
if SL.config["prefix_random"]
|
101
|
+
if input =~ /\[(\d{4}-)\d+\]: \S+/
|
102
|
+
Regexp.last_match(1)
|
103
|
+
else
|
104
|
+
format("%04d-", rand(9999))
|
105
|
+
end
|
106
|
+
else
|
107
|
+
""
|
108
|
+
end
|
109
|
+
end
|
129
110
|
|
130
|
-
|
131
|
-
|
111
|
+
def parse_single_line(input)
|
112
|
+
link_only = false
|
113
|
+
SL.clipboard = false
|
132
114
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
match_before = this_match.pre_match
|
115
|
+
res = parse_arguments(input.strip!).strip
|
116
|
+
input = res.nil? ? input.strip : res
|
117
|
+
query, input = input.extract_query({})
|
137
118
|
|
138
|
-
|
139
|
-
|
119
|
+
# if the end of input contain "^", copy to clipboard instead of STDOUT
|
120
|
+
SL.clipboard = true if input =~ /\^[!~:\s]*$/
|
140
121
|
|
141
|
-
|
142
|
-
|
143
|
-
invalid_search = true
|
144
|
-
end
|
122
|
+
# if the end of input contains "!!", only print the url
|
123
|
+
link_only = true if input =~ /!![\^~:\s]*$/
|
145
124
|
|
146
|
-
|
147
|
-
$stderr.print("\033[0K\rProcessed: #{counter_links} of #{total_links}, #{counter_errors} errors. ") unless SILENT
|
125
|
+
reference_link = input =~ /:([!\^~\s]*)$/
|
148
126
|
|
149
|
-
|
150
|
-
|
151
|
-
|
127
|
+
# if end of input contains ~, pull url from clipboard
|
128
|
+
if input =~ /~[:\^!\s]*$/
|
129
|
+
input.sub!(/[:!\^\s~]*$/, "")
|
130
|
+
clipboard = `__CF_USER_TEXT_ENCODING=$UID:0x8000100:0x8000100 pbpaste`.strip
|
131
|
+
if SL::URL.url?(clipboard)
|
132
|
+
type = reference_link ? :ref_title : :inline
|
133
|
+
print SL.make_link(type, input.strip, clipboard)
|
134
|
+
else
|
135
|
+
print SL.originput
|
136
|
+
end
|
137
|
+
Process.exit
|
138
|
+
end
|
152
139
|
|
153
|
-
|
154
|
-
link_info.gsub!(/"(.*?)"/) do
|
155
|
-
m = Regexp.last_match
|
156
|
-
@link_text = m[1] if @link_text == ""
|
157
|
-
m[0]
|
158
|
-
end
|
159
|
-
end
|
140
|
+
input.sub!(/[:!\^\s~]*$/, "")
|
160
141
|
|
161
|
-
|
162
|
-
|
163
|
-
|
142
|
+
## Maybe if input is just a URL, convert it to a link
|
143
|
+
## using hostname as text without doing search
|
144
|
+
if SL::URL.only_url?(input.strip)
|
145
|
+
type = reference_link ? :ref_title : :inline
|
146
|
+
@url, title = SL::URL.url_to_link(input.strip, type)
|
147
|
+
print SL.make_link(type, title, @url, title: false, force_title: false)
|
148
|
+
Process.exit
|
149
|
+
end
|
164
150
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
151
|
+
# check for additional search terms in parenthesis
|
152
|
+
additional_terms = ""
|
153
|
+
if input =~ /\((.*?)\)/
|
154
|
+
additional_terms = " #{Regexp.last_match(1).strip}"
|
155
|
+
input.sub!(/\(.*?\)/, "")
|
156
|
+
end
|
169
157
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
158
|
+
# Maybe detect "search + addition terms" and remove additional terms from link text?
|
159
|
+
# if input =~ /\+(.+?)$/
|
160
|
+
# additional_terms = "#{additional_terms} #{Regexp.last_match(1).strip}"
|
161
|
+
# input.sub!(/\+.*?$/, '').strip!
|
162
|
+
# end
|
175
163
|
|
176
|
-
|
177
|
-
search_type = Regexp.last_match(1).extract_shortener
|
178
|
-
unless SL::Searches.valid_search?(search_type) || search_type =~ /^(\S+\.)+\S+$/
|
179
|
-
SL.add_error("Invalid search#{SL::Searches.did_you_mean(search_type)}", match)
|
180
|
-
invalid_search = true
|
181
|
-
end
|
182
|
-
end
|
164
|
+
@link_text = false
|
183
165
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
create_footnote(m)
|
189
|
-
# Handle [](URL) and [%](URL), filling in title
|
190
|
-
elsif (@link_text == "" || @link_text == "%") && SL::URL.url?(link_info)
|
191
|
-
add_title(link_info)
|
192
|
-
elsif (@link_text == "" && link_info == "") || SL::URL.url?(link_info)
|
193
|
-
SL.add_error("Invalid search", match) unless SL::URL.url?(link_info)
|
194
|
-
match
|
195
|
-
else
|
196
|
-
link_info = @link_text if !@link_text.empty? && link_info == ""
|
166
|
+
if input =~ /"(.*?)"/
|
167
|
+
@link_text = Regexp.last_match(1)
|
168
|
+
input.gsub!(/"(.*?)"/, '\1')
|
169
|
+
end
|
197
170
|
|
198
|
-
|
199
|
-
|
200
|
-
link_only = false
|
201
|
-
SL.clipboard = false
|
202
|
-
SL.titleize = SL.config["empty_uses_page_title"]
|
171
|
+
# remove quotes from terms, just in case
|
172
|
+
# input.sub!(/^(!\S+)?\s*(["'])(.*?)\2([\!\^]+)?$/, "\\1 \\3\\4")
|
203
173
|
|
204
|
-
|
205
|
-
|
174
|
+
case input
|
175
|
+
when /^!(\S+)\s+(.*)$/
|
176
|
+
type = Regexp.last_match(1).extract_shortener
|
177
|
+
link_info = Regexp.last_match(2).strip
|
206
178
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
m[1]
|
211
|
-
end
|
179
|
+
@link_text ||= link_info
|
180
|
+
terms = link_info + additional_terms
|
181
|
+
terms.strip!
|
212
182
|
|
213
|
-
|
183
|
+
if SL::Searches.valid_search?(type) || type =~ /^(\S+\.)+\S+$/
|
184
|
+
if type && terms && !terms.empty?
|
185
|
+
# Iterate through custom searches for a match, perform search if matched
|
186
|
+
type, terms = custom_search(type, terms)
|
187
|
+
end
|
214
188
|
|
215
|
-
|
216
|
-
|
189
|
+
# if contains TLD, use site-specific search
|
190
|
+
if type =~ /^(\S+\.)+\S+$/
|
191
|
+
terms = "site:#{type} #{terms}"
|
192
|
+
type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
193
|
+
end
|
194
|
+
@search_count ||= 0
|
195
|
+
@search_count += 1
|
217
196
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
197
|
+
SL.add_query(query) if query
|
198
|
+
@url, title, @link_text = do_search(type, terms, @link_text, @search_count)
|
199
|
+
else
|
200
|
+
SL.add_error("Invalid search#{SL::Searches.did_you_mean(type)}", input)
|
201
|
+
SL.error_count
|
202
|
+
end
|
203
|
+
# Social handle expansion
|
204
|
+
when /^([tfilm])?@(\S+)\s*$/
|
205
|
+
type = Regexp.last_match(1)
|
206
|
+
type ||= if Regexp.last_match(2) =~ /[a-z0-9_]@[a-z0-9_.]+/i
|
207
|
+
"m"
|
208
|
+
else
|
209
|
+
"t"
|
210
|
+
end
|
211
|
+
@link_text = input.sub(/^[tfilm]/, "")
|
212
|
+
SL.add_query(query) if query
|
213
|
+
@url, title = SL::SocialSearch.social_handle(type, @link_text)
|
214
|
+
@link_text = title
|
215
|
+
else
|
216
|
+
SL.add_query(query) if query
|
217
|
+
@link_text ||= input
|
218
|
+
@url, title, @link_text = SL.ddg(input, @link_text)
|
219
|
+
end
|
223
220
|
|
224
|
-
|
221
|
+
if @url
|
222
|
+
res = confirmed?(@url)
|
223
|
+
if res
|
224
|
+
if res.is_a?(String) && SL::URL.url?(res)
|
225
|
+
@url = res
|
226
|
+
title = SL::URL.title(@url) if SL.titleize && title == ""
|
227
|
+
end
|
225
228
|
|
226
|
-
|
227
|
-
|
229
|
+
if type =~ /sp(ell)?/
|
230
|
+
SL.add_output(@url)
|
231
|
+
elsif link_only
|
232
|
+
SL.add_output(@url)
|
233
|
+
elsif @url == "embed"
|
234
|
+
SL.add_output(title)
|
235
|
+
else
|
236
|
+
type = reference_link ? :ref_title : :inline
|
228
237
|
|
229
|
-
|
230
|
-
|
238
|
+
SL.add_output SL.make_link(type, @link_text, @url, title: title, force_title: false)
|
239
|
+
SL.print_errors
|
240
|
+
end
|
241
|
+
else
|
242
|
+
SL.add_error("Canceled", "User canceled result #{@url}")
|
243
|
+
SL.add_output SL.originput.chomp
|
244
|
+
SL.print_errors
|
245
|
+
end
|
246
|
+
else
|
247
|
+
SL.add_error("No results", title)
|
248
|
+
SL.add_output SL.originput.chomp
|
249
|
+
SL.print_errors
|
250
|
+
end
|
231
251
|
|
232
|
-
|
233
|
-
link_only = true if search_terms =~ /!!\^?$/
|
252
|
+
return unless SL.clipboard
|
234
253
|
|
235
|
-
|
254
|
+
if SL.output == SL.originput
|
255
|
+
warn "No results found"
|
256
|
+
else
|
257
|
+
`echo #{Shellwords.escape(SL.output.join(""))}|tr -d "\n"|pbcopy`
|
258
|
+
warn "Results in clipboard"
|
259
|
+
end
|
260
|
+
end
|
236
261
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
end
|
241
|
-
elsif link_info =~ /^!/
|
242
|
-
search_word = link_info.match(/^!(\S+)/)
|
243
|
-
st = search_word[1].extract_shortener
|
244
|
-
if search_word && SL::Searches.valid_search?(st)
|
245
|
-
search_type = st unless search_word.nil?
|
246
|
-
search_terms = @link_text
|
247
|
-
elsif search_word && st =~ /^(\S+\.)+\S+$/
|
248
|
-
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
249
|
-
search_terms = "site:#{search_word[1]} #{@link_text}"
|
250
|
-
else
|
251
|
-
SL.add_error("Invalid search#{SL::Searches.did_you_mean(st)}", match)
|
252
|
-
search_type = false
|
253
|
-
search_terms = false
|
254
|
-
end
|
255
|
-
elsif @link_text && !@link_text.empty? && (!link_info || link_info.empty?)
|
256
|
-
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
257
|
-
search_terms = @link_text
|
258
|
-
elsif link_info && !link_info.empty?
|
259
|
-
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
260
|
-
search_terms = link_info
|
261
|
-
else
|
262
|
-
SL.add_error("Invalid search", match)
|
263
|
-
search_type = false
|
264
|
-
search_terms = false
|
265
|
-
end
|
262
|
+
def parse_brackets(input)
|
263
|
+
lines = input.split("\n")
|
264
|
+
out = []
|
266
265
|
|
267
|
-
|
266
|
+
total_links = 0
|
267
|
+
in_code_block = false
|
268
|
+
in_list = false
|
269
|
+
last_indent = 0
|
270
|
+
line_difference = 0
|
271
|
+
counter_links = 0
|
272
|
+
lines.each_with_index do |line, num|
|
273
|
+
SL.line_num = num - line_difference
|
274
|
+
@cursor_difference = 0
|
268
275
|
|
269
|
-
|
276
|
+
if line =~ /^\s*[~`]{3,}/
|
277
|
+
if in_code_block
|
278
|
+
in_code_block = false
|
279
|
+
out.push(line)
|
280
|
+
next
|
281
|
+
else
|
282
|
+
in_code_block = true
|
283
|
+
end
|
284
|
+
elsif in_code_block
|
285
|
+
out.push(line)
|
286
|
+
next
|
287
|
+
elsif line.strip.empty?
|
288
|
+
out.push(line)
|
289
|
+
next
|
290
|
+
elsif line =~ /^\s*([-+*]|\d+\.)\s/
|
291
|
+
in_list = true
|
292
|
+
last_indent = line.indent_level
|
293
|
+
elsif in_list && line.indent_level > last_indent + 2
|
294
|
+
out.push(line)
|
295
|
+
next
|
296
|
+
elsif in_list && line.indent_level < last_indent && line !~ /^\s*([-+*]|\d+\.)\s/
|
297
|
+
in_list = false
|
298
|
+
elsif in_list && line.indent_level == last_indent + 1
|
299
|
+
# noop
|
300
|
+
else
|
301
|
+
in_list = false
|
302
|
+
# ignore links in code blocks
|
303
|
+
if line =~ /^(( {4,}|\t+)[^*+-])/
|
304
|
+
last_indent = line.indent_level
|
305
|
+
out.push(line)
|
306
|
+
next
|
307
|
+
end
|
308
|
+
last_indent = line.indent_level
|
309
|
+
end
|
270
310
|
|
271
|
-
|
272
|
-
# warn "Searching #{search_type} for #{search_terms}"
|
311
|
+
total_links += line.count_links
|
273
312
|
|
274
|
-
|
275
|
-
@url, title, @link_text = do_search(search_type, search_terms, @link_text, @search_count)
|
313
|
+
@delete_line = false
|
276
314
|
|
277
|
-
|
278
|
-
if title
|
279
|
-
@link_text = title
|
280
|
-
else
|
281
|
-
add_title(@url)
|
282
|
-
end
|
283
|
-
end
|
315
|
+
@search_count = 0
|
284
316
|
|
285
|
-
|
286
|
-
|
287
|
-
return match unless res
|
288
|
-
|
289
|
-
@url = res if res.is_a?(String) && SL::URL.url?(res)
|
290
|
-
|
291
|
-
title = SL::URL.title(@url) if SL.titleize && (title.nil? || title.empty?)
|
292
|
-
|
293
|
-
@link_text = title if @link_text == "" && title
|
294
|
-
force_title = search_type =~ /def/ ? true : false
|
295
|
-
|
296
|
-
if link_only || search_type =~ /sp(ell)?/ || @url == "embed"
|
297
|
-
@url = title if @url == "embed"
|
298
|
-
@cursor_difference += SL.match_length - @url.length
|
299
|
-
SL.match_length = @url.length
|
300
|
-
SL.add_report("#{@match_string} => #{@url}")
|
301
|
-
@url
|
302
|
-
elsif @ref_title
|
303
|
-
unless @links.key? @url
|
304
|
-
@links[@url] = @link_text
|
305
|
-
SL.add_footer SL.make_link(:ref_title, @link_text, @url, title: title, force_title: force_title)
|
306
|
-
end
|
307
|
-
@delete_line = true
|
308
|
-
elsif SL.config["inline"]
|
309
|
-
res = SL.make_link(:inline, @link_text, @url, title: title, force_title: force_title)
|
310
|
-
@cursor_difference += SL.match_length - res.length
|
311
|
-
SL.match_length = res.length
|
312
|
-
SL.add_report("#{@match_string} => #{@url}")
|
313
|
-
res
|
314
|
-
else
|
315
|
-
unless @links.key? @url
|
316
|
-
@highest_marker += 1
|
317
|
-
@links[@url] = format("%<pre>s%<m>04d", pre: @prefix, m: @highest_marker)
|
318
|
-
SL.add_footer SL.make_link(:ref_title, @links[@url], @url, title: title, force_title: force_title)
|
319
|
-
end
|
320
|
-
|
321
|
-
type = SL.config["inline"] ? :inline : :ref_link
|
322
|
-
res = SL.make_link(type, @link_text, @links[@url], title: false, force_title: force_title)
|
323
|
-
@cursor_difference += SL.match_length - res.length
|
324
|
-
SL.match_length = res.length
|
325
|
-
SL.add_report("#{@match_string} => #{@url}")
|
326
|
-
res
|
327
|
-
end
|
328
|
-
else
|
329
|
-
SL.add_error("No results", "#{search_terms} (#{@match_string})")
|
330
|
-
counter_errors += 1
|
331
|
-
match
|
332
|
-
end
|
333
|
-
else
|
334
|
-
SL.add_error("Invalid search", match)
|
335
|
-
counter_errors += 1
|
336
|
-
match
|
337
|
-
end
|
338
|
-
end
|
339
|
-
end
|
340
|
-
line_difference += 1 if @delete_line
|
341
|
-
out.push(line) unless @delete_line
|
342
|
-
@delete_line = false
|
343
|
-
end
|
344
|
-
warn "\n" unless SILENT
|
317
|
+
line.gsub!(/\[(.*?)\]\((.*?)\)/) do |match|
|
318
|
+
this_match = Regexp.last_match
|
345
319
|
|
346
|
-
|
320
|
+
SL.match_column = this_match.begin(0) - @cursor_difference
|
321
|
+
@match_string = this_match.to_s
|
322
|
+
SL.match_length = @match_string.length
|
323
|
+
match_before = this_match.pre_match
|
347
324
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
last_line = input.strip.split("\n")[-1]
|
355
|
-
case last_line
|
356
|
-
when /^\[.*?\]: http/
|
357
|
-
SL.add_output "#{input.rstrip}\n"
|
358
|
-
when /^\[\^.*?\]: /
|
359
|
-
SL.add_output input.rstrip
|
360
|
-
else
|
361
|
-
SL.add_output "#{input}\n\n"
|
325
|
+
invalid_search = false
|
326
|
+
@ref_title = false
|
327
|
+
|
328
|
+
if match_before.scan(/(^|[^\\])`/).length.odd?
|
329
|
+
SL.add_report("Match '#{@match_string}' within an inline code block")
|
330
|
+
invalid_search = true
|
362
331
|
end
|
363
|
-
|
364
|
-
|
332
|
+
counter_links += 1
|
333
|
+
$stderr.print("\033[0K\rProcessed: #{counter_links} of #{total_links}, #{SL.error_count} errors. ") unless SILENT
|
365
334
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
SL.print_errors
|
370
|
-
else # Assume single line input
|
371
|
-
link_only = false
|
372
|
-
SL.clipboard = false
|
335
|
+
@link_text = this_match[1] || ""
|
336
|
+
link_info = parse_arguments(this_match[2].strip).strip || ""
|
337
|
+
query, link_info = link_info.extract_query({})
|
373
338
|
|
374
|
-
|
375
|
-
|
376
|
-
|
339
|
+
if @link_text.strip == "" && link_info =~ /".*?"/
|
340
|
+
link_info.gsub!(/"(.*?)"/) do
|
341
|
+
m = Regexp.last_match
|
342
|
+
@link_text = m[1] if @link_text == ""
|
343
|
+
m[0]
|
344
|
+
end
|
345
|
+
end
|
377
346
|
|
378
|
-
|
379
|
-
|
347
|
+
link_info.gsub!(/<(.*?)>/) do
|
348
|
+
%(%22#{Regexp.last_match(1)}%22)
|
349
|
+
end
|
380
350
|
|
381
|
-
|
382
|
-
|
351
|
+
if link_info.strip =~ /:$/ && line.strip == match
|
352
|
+
@ref_title = true
|
353
|
+
link_info.sub!(/\s*:\s*$/, "")
|
354
|
+
end
|
383
355
|
|
384
|
-
|
356
|
+
if @link_text.empty? && link_info.sub(/^[!\^]\S+/, "").strip.empty?
|
357
|
+
SL.add_error("No input", match)
|
358
|
+
invalid_search = true
|
359
|
+
end
|
385
360
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
print SL.make_link(type, input.strip, clipboard)
|
393
|
-
else
|
394
|
-
print SL.originput
|
361
|
+
if link_info =~ /^!(\S+)/
|
362
|
+
search_type = Regexp.last_match(1).extract_shortener
|
363
|
+
unless SL::Searches.valid_search?(search_type) || search_type =~ /^(\S+\.)+\S+$/
|
364
|
+
SL.add_error("Invalid search#{SL::Searches.did_you_mean(search_type)}", match)
|
365
|
+
invalid_search = true
|
366
|
+
end
|
395
367
|
end
|
396
|
-
Process.exit
|
397
|
-
end
|
398
368
|
|
399
|
-
|
369
|
+
if invalid_search
|
370
|
+
match
|
371
|
+
elsif link_info =~ /^\^(.+)/
|
372
|
+
m = Regexp.last_match
|
373
|
+
create_footnote(m)
|
374
|
+
# Handle [](URL) and [%](URL), filling in title
|
375
|
+
elsif (@link_text == "" || @link_text == "%") && SL::URL.url?(link_info)
|
376
|
+
add_title(link_info)
|
377
|
+
elsif (@link_text == "" && link_info == "") || SL::URL.url?(link_info)
|
378
|
+
SL.add_error("Invalid search", match) unless SL::URL.url?(link_info)
|
379
|
+
match
|
380
|
+
else
|
381
|
+
link_info = @link_text if !@link_text.empty? && link_info == ""
|
400
382
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
print SL.make_link(type, title, @url, title: false, force_title: false)
|
407
|
-
Process.exit
|
408
|
-
end
|
383
|
+
search_type = ""
|
384
|
+
search_terms = ""
|
385
|
+
link_only = false
|
386
|
+
SL.clipboard = false
|
387
|
+
SL.titleize = SL.config["empty_uses_page_title"]
|
409
388
|
|
410
|
-
|
411
|
-
|
412
|
-
if input =~ /\((.*?)\)/
|
413
|
-
additional_terms = " #{Regexp.last_match(1).strip}"
|
414
|
-
input.sub!(/\(.*?\)/, "")
|
415
|
-
end
|
389
|
+
if link_info =~ /^(?:[!\^](\S+))\s*(.*)$/
|
390
|
+
m = Regexp.last_match
|
416
391
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
392
|
+
search_type = if m[1].nil?
|
393
|
+
SL::GoogleSearch.api_key? ? "gg" : "g"
|
394
|
+
else
|
395
|
+
m[1]
|
396
|
+
end
|
422
397
|
|
423
|
-
|
398
|
+
search_type.extract_shortener!
|
424
399
|
|
425
|
-
|
426
|
-
|
427
|
-
input.gsub!(/"(.*?)"/, '\1')
|
428
|
-
end
|
400
|
+
search_terms = m[2].gsub(/(^["']|["']$)/, "")
|
401
|
+
search_terms.strip!
|
429
402
|
|
430
|
-
|
431
|
-
|
403
|
+
# if the link text is just '%' replace with title regardless of config settings
|
404
|
+
if @link_text == "%" && search_terms && !search_terms.empty?
|
405
|
+
SL.titleize = true
|
406
|
+
@link_text = ""
|
407
|
+
end
|
432
408
|
|
433
|
-
|
434
|
-
when /^!(\S+)\s+(.*)$/
|
435
|
-
type = Regexp.last_match(1).extract_shortener
|
436
|
-
link_info = Regexp.last_match(2).strip
|
409
|
+
search_terms = @link_text if search_terms == ""
|
437
410
|
|
438
|
-
|
439
|
-
|
440
|
-
terms.strip!
|
411
|
+
# if the input starts with a +, append it to the link text as the search terms
|
412
|
+
search_terms = "#{@link_text} #{search_terms.strip.sub(/^\+\s*/, '')}" if search_terms.strip =~ /^\+[^+]/
|
441
413
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
414
|
+
# if the end of input contain "^", copy to clipboard instead of STDOUT
|
415
|
+
SL.clipboard = true if search_terms =~ /(!!)?\^(!!)?$/
|
416
|
+
|
417
|
+
# if the end of input contains "!!", only print the url
|
418
|
+
link_only = true if search_terms =~ /!!\^?$/
|
447
419
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
420
|
+
search_terms = search_terms.sub(/(!!)?\^?(!!)?$/, "")
|
421
|
+
|
422
|
+
if search_type =~ /^(\S+\.)+\S+$/
|
423
|
+
search_type = "g"
|
424
|
+
search_terms = "site:#{m[1]} #{search_terms}"
|
425
|
+
end
|
426
|
+
elsif link_info =~ /^!/
|
427
|
+
search_word = link_info.match(/^!(\S+)/)
|
428
|
+
st = search_word[1].extract_shortener
|
429
|
+
if search_word && SL::Searches.valid_search?(st)
|
430
|
+
search_type = st unless search_word.nil?
|
431
|
+
search_terms = @link_text
|
432
|
+
elsif search_word && st =~ /^(\S+\.)+\S+$/
|
433
|
+
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
434
|
+
search_terms = "site:#{search_word[1]} #{@link_text}"
|
435
|
+
else
|
436
|
+
SL.add_error("Invalid search#{SL::Searches.did_you_mean(st)}", match)
|
437
|
+
search_type = false
|
438
|
+
search_terms = false
|
439
|
+
end
|
440
|
+
elsif @link_text && !@link_text.empty? && (!link_info || link_info.empty?)
|
441
|
+
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
442
|
+
search_terms = @link_text
|
443
|
+
elsif link_info && !link_info.empty?
|
444
|
+
search_type = SL::GoogleSearch.api_key? ? "gg" : "g"
|
445
|
+
search_terms = link_info
|
446
|
+
else
|
447
|
+
SL.add_error("Invalid search", match)
|
448
|
+
search_type = false
|
449
|
+
search_terms = false
|
452
450
|
end
|
453
|
-
|
454
|
-
|
451
|
+
|
452
|
+
search_type, search_terms = custom_search(search_type, search_terms) if search_type && !search_terms.empty?
|
455
453
|
|
456
454
|
SL.add_query(query) if query
|
457
|
-
@url, title, @link_text = do_search(type, terms, @link_text, @search_count)
|
458
|
-
else
|
459
|
-
SL.add_error("Invalid search#{SL::Searches.did_you_mean(type)}", input)
|
460
|
-
counter_errors += 1
|
461
|
-
end
|
462
|
-
# Social handle expansion
|
463
|
-
when /^([tfilm])?@(\S+)\s*$/
|
464
|
-
type = Regexp.last_match(1)
|
465
|
-
type ||= if Regexp.last_match(2) =~ /[a-z0-9_]@[a-z0-9_.]+/i
|
466
|
-
"m"
|
467
|
-
else
|
468
|
-
"t"
|
469
|
-
end
|
470
|
-
@link_text = input.sub(/^[tfilm]/, "")
|
471
|
-
SL.add_query(query) if query
|
472
|
-
@url, title = SL::SocialSearch.social_handle(type, @link_text)
|
473
|
-
@link_text = title
|
474
|
-
else
|
475
|
-
SL.add_query(query) if query
|
476
|
-
@link_text ||= input
|
477
|
-
@url, title, @link_text = SL.ddg(input, @link_text)
|
478
|
-
end
|
479
455
|
|
480
|
-
|
481
|
-
|
482
|
-
if res
|
483
|
-
if res.is_a?(String) && SL::URL.url?(res)
|
484
|
-
@url = res
|
485
|
-
title = SL::URL.title(@url) if SL.titleize && title == ""
|
486
|
-
end
|
456
|
+
if (search_type && search_terms) || @url
|
457
|
+
# warn "Searching #{search_type} for #{search_terms}"
|
487
458
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
459
|
+
@search_count += 1
|
460
|
+
@url, title, @link_text = do_search(search_type, search_terms, @link_text, @search_count)
|
461
|
+
|
462
|
+
if (@link_text == "" || @link_text == "%") && @url
|
463
|
+
if title
|
464
|
+
@link_text = title
|
465
|
+
else
|
466
|
+
add_title(@url)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
if @url
|
471
|
+
res = confirmed?(@url)
|
472
|
+
return match unless res
|
496
473
|
|
497
|
-
|
498
|
-
|
474
|
+
@url = res if res.is_a?(String) && SL::URL.url?(res)
|
475
|
+
|
476
|
+
title = SL::URL.title(@url) if SL.titleize && (title.nil? || title.empty?)
|
477
|
+
|
478
|
+
@link_text = title if @link_text == "" && title
|
479
|
+
force_title = search_type =~ /def/ ? true : false
|
480
|
+
|
481
|
+
if link_only || search_type =~ /sp(ell)?/ || @url == "embed"
|
482
|
+
@url = title if @url == "embed"
|
483
|
+
@cursor_difference += SL.match_length - @url.length
|
484
|
+
SL.match_length = @url.length
|
485
|
+
SL.add_report("#{@match_string} => #{@url}")
|
486
|
+
@url
|
487
|
+
elsif @ref_title
|
488
|
+
unless @links.key? @url
|
489
|
+
@links[@url] = @link_text
|
490
|
+
SL.add_footer SL.make_link(:ref_title, @link_text, @url, title: title, force_title: force_title)
|
491
|
+
end
|
492
|
+
@delete_line = true
|
493
|
+
elsif SL.config["inline"]
|
494
|
+
res = SL.make_link(:inline, @link_text, @url, title: title, force_title: force_title)
|
495
|
+
@cursor_difference += SL.match_length - res.length
|
496
|
+
SL.match_length = res.length
|
497
|
+
SL.add_report("#{@match_string} => #{@url}")
|
498
|
+
res
|
499
|
+
else
|
500
|
+
unless @links.key? @url
|
501
|
+
@highest_marker += 1
|
502
|
+
@links[@url] = format("%<pre>s%<m>04d", pre: @prefix, m: @highest_marker)
|
503
|
+
SL.add_footer SL.make_link(:ref_title, @links[@url], @url, title: title, force_title: force_title)
|
504
|
+
end
|
505
|
+
|
506
|
+
type = SL.config["inline"] ? :inline : :ref_link
|
507
|
+
res = SL.make_link(type, @link_text, @links[@url], title: false, force_title: force_title)
|
508
|
+
@cursor_difference += SL.match_length - res.length
|
509
|
+
SL.match_length = res.length
|
510
|
+
SL.add_report("#{@match_string} => #{@url}")
|
511
|
+
res
|
512
|
+
end
|
513
|
+
else
|
514
|
+
SL.add_error("No results", "#{search_terms} (#{@match_string})")
|
515
|
+
match
|
516
|
+
end
|
517
|
+
else
|
518
|
+
SL.add_error("Invalid search", match)
|
519
|
+
match
|
499
520
|
end
|
500
|
-
else
|
501
|
-
SL.add_error("Canceled", "User canceled result #{@url}")
|
502
|
-
SL.add_output SL.originput.chomp
|
503
|
-
SL.print_errors
|
504
521
|
end
|
505
|
-
else
|
506
|
-
SL.add_error("No results", title)
|
507
|
-
SL.add_output SL.originput.chomp
|
508
|
-
SL.print_errors
|
509
522
|
end
|
523
|
+
line_difference += 1 if @delete_line
|
524
|
+
out.push(line) unless @delete_line
|
525
|
+
@delete_line = false
|
526
|
+
end
|
527
|
+
warn "\n" unless SILENT
|
510
528
|
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
529
|
+
input = out.delete_if { |l| l.strip =~ /^<!--DELETE-->$/ }.join("\n")
|
530
|
+
|
531
|
+
if SL.config["inline"]
|
532
|
+
SL.add_output "#{input}\n"
|
533
|
+
SL.add_output "\n#{SL.print_footer}" unless SL.footer.empty?
|
534
|
+
elsif SL.footer.empty?
|
535
|
+
SL.add_output input
|
536
|
+
else
|
537
|
+
last_line = input.strip.split("\n")[-1]
|
538
|
+
case last_line
|
539
|
+
when /^\[.*?\]: http/
|
540
|
+
SL.add_output "#{input.rstrip}\n"
|
541
|
+
when /^\[\^.*?\]: /
|
542
|
+
SL.add_output input.rstrip
|
543
|
+
else
|
544
|
+
SL.add_output "#{input}\n\n"
|
518
545
|
end
|
546
|
+
SL.add_output "#{SL.print_footer}\n\n"
|
519
547
|
end
|
520
|
-
end
|
521
548
|
|
522
|
-
|
549
|
+
SL.line_num = nil
|
550
|
+
SL.add_report("Processed: #{total_links} links, #{SL.error_count} errors.")
|
551
|
+
SL.print_report
|
552
|
+
SL.print_errors
|
553
|
+
end
|
523
554
|
|
524
555
|
def add_title(link_info)
|
525
556
|
@url = link_info
|
@@ -67,7 +67,7 @@ module SL
|
|
67
67
|
output_title = result["title"]
|
68
68
|
output_title.remove_seo!(output_url) if SL.config["remove_seo"]
|
69
69
|
|
70
|
-
output_url = SL.first_image if search_type =~ /img$/
|
70
|
+
output_url = SL.first_image(output_url) if search_type =~ /img$/
|
71
71
|
|
72
72
|
[output_url, output_title, link_text]
|
73
73
|
rescue StandardError
|
data/lib/searchlink/string.rb
CHANGED
@@ -3,6 +3,32 @@
|
|
3
3
|
module SL
|
4
4
|
# String helpers
|
5
5
|
class ::String
|
6
|
+
# Scan a string for links
|
7
|
+
# @return [Hash] Hash of links
|
8
|
+
def scan_links
|
9
|
+
links = {}
|
10
|
+
scan(/\[(.*?)\]:\s+(.*?)\n/).each { |match| links[match[1].strip] = match[0] }
|
11
|
+
links
|
12
|
+
end
|
13
|
+
|
14
|
+
# Count the indent level of a string
|
15
|
+
# @return [Integer] The indent level
|
16
|
+
def indent_level
|
17
|
+
return 0 if empty?
|
18
|
+
|
19
|
+
gsub!(/^ /, "\t") while self =~ /^ /
|
20
|
+
indent = match(/^\t+/)
|
21
|
+
return 0 unless indent
|
22
|
+
|
23
|
+
indent[0].length
|
24
|
+
end
|
25
|
+
|
26
|
+
# Count the links in a string
|
27
|
+
# @return [Integer] The number of links
|
28
|
+
def count_links
|
29
|
+
scan(/\[(.*?)\]\((.*?)\)/).length
|
30
|
+
end
|
31
|
+
|
6
32
|
# Quote a YAML value if needed
|
7
33
|
def yaml_val
|
8
34
|
yaml = YAML.safe_load("key: '#{self}'")
|
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.88
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: base64
|