snibbets 2.0.39 → 2.0.40
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +179 -37
- data/lib/snibbets/string.rb +18 -2
- data/lib/snibbets/version.rb +1 -1
- data/lib/snibbets.rb +141 -136
- data/mise.toml +2 -0
- data/src/_README.md +176 -36
- metadata +4 -8
- data/Gemfile.lock +0 -125
- data/lib/snibbets/todo_spec.rb +0 -11
data/lib/snibbets.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require_relative
|
15
|
-
require_relative
|
16
|
-
require_relative
|
17
|
-
require_relative
|
18
|
-
require_relative
|
19
|
-
require_relative
|
20
|
-
require_relative
|
21
|
-
require_relative
|
22
|
-
require_relative
|
23
|
-
require_relative
|
24
|
-
require_relative
|
3
|
+
require "cgi"
|
4
|
+
require "erb"
|
5
|
+
require "fileutils"
|
6
|
+
require "json"
|
7
|
+
require "open3"
|
8
|
+
require "optparse"
|
9
|
+
require "readline"
|
10
|
+
require "shellwords"
|
11
|
+
require "tty-reader"
|
12
|
+
require "tty-which"
|
13
|
+
require "yaml"
|
14
|
+
require_relative "snibbets/version"
|
15
|
+
require_relative "snibbets/colors"
|
16
|
+
require_relative "snibbets/config"
|
17
|
+
require_relative "snibbets/which"
|
18
|
+
require_relative "snibbets/string"
|
19
|
+
require_relative "snibbets/hash"
|
20
|
+
require_relative "snibbets/array"
|
21
|
+
require_relative "snibbets/menu"
|
22
|
+
require_relative "snibbets/os"
|
23
|
+
require_relative "snibbets/highlight"
|
24
|
+
require_relative "snibbets/lexers"
|
25
25
|
|
26
26
|
# Top level module
|
27
27
|
module Snibbets
|
@@ -47,78 +47,84 @@ module Snibbets
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# Search the snippets directory for query using find and grep
|
50
|
-
def search(try: 0)
|
50
|
+
def search(try: 0, matches: [])
|
51
51
|
folder = File.expand_path(Snibbets.options[:source])
|
52
|
-
# start by doing a spotlight search
|
53
|
-
#
|
54
|
-
#
|
55
|
-
|
52
|
+
# start by doing a spotlight search with mdfind
|
53
|
+
# next try only search by filenames (find)
|
54
|
+
# next try search with grep (rg, ag, ack, grep)
|
55
|
+
# concatenate results from each search, removing duplicates and sorting
|
56
|
+
ext = Snibbets.options[:extension] || "md"
|
57
|
+
|
56
58
|
cmd = case try
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
59
|
+
when 1
|
60
|
+
%(find "#{folder}" -iregex '^#{Regexp.escape(folder)}/#{@query.rx}' -name '*.#{ext}')
|
61
|
+
when 2
|
62
|
+
rg = TTY::Which.which("rg")
|
63
|
+
ag = TTY::Which.which("ag")
|
64
|
+
ack = TTY::Which.which("ack")
|
65
|
+
grep = TTY::Which.which("grep")
|
66
|
+
if !rg.empty?
|
67
|
+
%(#{rg} -li --color=never --glob='*.#{ext}' '#{@query.rx}' "#{folder}")
|
68
|
+
elsif !ag.empty?
|
69
|
+
%(#{ag} -li --nocolor -G '.*.#{ext}' '#{@query.rx}' "#{folder}")
|
70
|
+
elsif !ack.empty?
|
71
|
+
%(#{ack} -li --nocolor --markdown '#{@query.rx}' "#{folder}")
|
72
|
+
elsif !grep.empty?
|
73
|
+
%(#{grep} -iEl '#{@query.rx}' "#{folder}"/**/*.#{ext})
|
74
|
+
else
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
else
|
78
|
+
mdfind = TTY::Which.which("mdfind")
|
79
|
+
if mdfind.nil? || mdfind.empty?
|
80
|
+
nil
|
81
|
+
else
|
82
|
+
name_only = Snibbets.options[:name_only] ? "-name " : ""
|
83
|
+
%(mdfind -onlyin #{folder} #{name_only}'#{@query} name:.#{ext}' 2>/dev/null)
|
84
|
+
end
|
85
|
+
end
|
84
86
|
|
85
87
|
if try == 2
|
86
88
|
if Snibbets.options[:name_only]
|
87
|
-
puts
|
89
|
+
puts "{br}No name matches found".x
|
88
90
|
Process.exit 1
|
89
91
|
elsif cmd.nil?
|
90
|
-
puts
|
92
|
+
puts "{br}No search method available on this system. Please install ripgrep, silver surfer, ack, or grep.".x
|
91
93
|
Process.exit 1
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
95
|
-
res = cmd.nil? ?
|
96
|
-
|
97
|
-
matches = []
|
97
|
+
res = cmd.nil? ? "" : `#{cmd}`.strip
|
98
98
|
|
99
99
|
unless res.empty?
|
100
100
|
lines = res.split(/\n/)
|
101
101
|
lines.each do |l|
|
102
102
|
matches << {
|
103
|
-
|
104
|
-
|
103
|
+
"title" => File.basename(l, ".*"),
|
104
|
+
"path" => l,
|
105
105
|
}
|
106
106
|
end
|
107
107
|
|
108
|
-
matches.sort_by! { |a| a[
|
108
|
+
matches.sort_by! { |a| a["title"] }.uniq!
|
109
|
+
|
110
|
+
# return matches unless matches.empty?
|
111
|
+
end
|
109
112
|
|
110
|
-
|
113
|
+
matches.delete_if do |m|
|
114
|
+
content = IO.read(m["path"])
|
115
|
+
!content.match_all_tags(@query)
|
111
116
|
end
|
112
117
|
|
118
|
+
# return after 3 cycles
|
113
119
|
return matches if try == 2
|
114
120
|
|
115
|
-
#
|
116
|
-
search(try: try + 1
|
121
|
+
# continue search, appends to current matches
|
122
|
+
search(try: try + 1, matches: matches)
|
117
123
|
end
|
118
124
|
|
119
125
|
def open_snippet_in_nvultra(filepath)
|
120
|
-
notebook = Snibbets.options[:source].gsub(/ /,
|
121
|
-
note = ERB::Util.url_encode(File.basename(filepath,
|
126
|
+
notebook = Snibbets.options[:source].gsub(/ /, "%20")
|
127
|
+
note = ERB::Util.url_encode(File.basename(filepath, ".md"))
|
122
128
|
url = "x-nvultra://open?notebook=#{notebook}¬e=#{note}"
|
123
129
|
`open '#{url}'`
|
124
130
|
end
|
@@ -126,7 +132,7 @@ module Snibbets
|
|
126
132
|
def open_snippet_in_editor(filepath)
|
127
133
|
editor = Snibbets.options[:editor] || Snibbets::Config.best_editor
|
128
134
|
|
129
|
-
os = RbConfig::CONFIG[
|
135
|
+
os = RbConfig::CONFIG["target_os"]
|
130
136
|
|
131
137
|
if editor.nil?
|
132
138
|
OS.open(filepath)
|
@@ -158,7 +164,7 @@ module Snibbets
|
|
158
164
|
def new_snippet_from_clipboard
|
159
165
|
return false unless $stdout.isatty
|
160
166
|
|
161
|
-
trap(
|
167
|
+
trap("SIGINT") do
|
162
168
|
Howzit.console.info "\nCancelled"
|
163
169
|
exit!
|
164
170
|
end
|
@@ -167,10 +173,10 @@ module Snibbets
|
|
167
173
|
reader = TTY::Reader.new
|
168
174
|
|
169
175
|
begin
|
170
|
-
input = reader.read_line(
|
176
|
+
input = reader.read_line("{by}What does this snippet do{bw}? ".x).strip
|
171
177
|
title = input unless input.empty?
|
172
178
|
|
173
|
-
input = reader.read_line(
|
179
|
+
input = reader.read_line("{by}What language(s) does it use ({xw}separate with spaces, full names or file extensions{by}){bw}? ".x).strip
|
174
180
|
langs = input.split(/ +/).map(&:strip) unless input.empty?
|
175
181
|
rescue TTY::Reader::InputInterrupt
|
176
182
|
puts "\nCancelled"
|
@@ -182,10 +188,10 @@ module Snibbets
|
|
182
188
|
|
183
189
|
exts = langs if exts.empty?
|
184
190
|
|
185
|
-
filename = "#{title}#{exts.map { |x| ".#{x}" }.join(
|
191
|
+
filename = "#{title}#{exts.map { |x| ".#{x}" }.join("")}.#{Snibbets.options[:extension]}"
|
186
192
|
filepath = File.join(File.expand_path(Snibbets.options[:source]), filename.escape_filename)
|
187
|
-
File.open(filepath,
|
188
|
-
f.puts "tags: #{tags.join(
|
193
|
+
File.open(filepath, "w") do |f|
|
194
|
+
f.puts "tags: #{tags.join(", ")}
|
189
195
|
|
190
196
|
```
|
191
197
|
#{pb}
|
@@ -201,20 +207,20 @@ module Snibbets
|
|
201
207
|
def new_snippet_with_editor(options)
|
202
208
|
return false unless $stdout.isatty
|
203
209
|
|
204
|
-
trap(
|
210
|
+
trap("SIGINT") do
|
205
211
|
Howzit.console.info "\nCancelled"
|
206
212
|
exit!
|
207
213
|
end
|
208
214
|
|
209
215
|
reader = TTY::Reader.new
|
210
216
|
if options[:filename]
|
211
|
-
title = options[:filename].sub(/(\.#{Snibbets.options[:extension]})$/,
|
217
|
+
title = options[:filename].sub(/(\.#{Snibbets.options[:extension]})$/, "")
|
212
218
|
extensions = options[:filename].match(/(\.\w+)+$/)
|
213
219
|
langs = extensions ? extensions[0].split(/\./).delete_if(&:empty?) : []
|
214
|
-
title.sub!(/(\.\w+)+$/,
|
220
|
+
title.sub!(/(\.\w+)+$/, "")
|
215
221
|
else
|
216
222
|
begin
|
217
|
-
input = reader.read_line(
|
223
|
+
input = reader.read_line("{by}What does this snippet do{bw}? ".x).strip
|
218
224
|
title = input unless input.empty?
|
219
225
|
rescue TTY::Reader::InputInterrupt
|
220
226
|
puts "\nCancelled"
|
@@ -225,7 +231,7 @@ module Snibbets
|
|
225
231
|
if langs.nil? || langs.empty?
|
226
232
|
begin
|
227
233
|
# printf 'What language(s) does it use (separate with spaces, full names or file extensions will work)? '
|
228
|
-
input = reader.read_line(
|
234
|
+
input = reader.read_line("{by}What language(s) does it use ({xw}separate with spaces, full names or file extensions{by}){bw}? ".x).strip
|
229
235
|
# input = $stdin.gets.chomp
|
230
236
|
langs = input.split(/ +/).map(&:strip) unless input.empty?
|
231
237
|
rescue TTY::Reader::InputInterrupt
|
@@ -239,11 +245,11 @@ module Snibbets
|
|
239
245
|
|
240
246
|
exts = langs if exts.empty?
|
241
247
|
|
242
|
-
filename = "#{title}#{exts.map { |x| ".#{x}" }.join(
|
248
|
+
filename = "#{title}#{exts.map { |x| ".#{x}" }.join("")}.#{Snibbets.options[:extension]}"
|
243
249
|
filepath = File.join(File.expand_path(Snibbets.options[:source]), filename.escape_filename)
|
244
250
|
|
245
|
-
output
|
246
|
-
tags: #{tags.join(
|
251
|
+
output = <<~EOOUTPUT
|
252
|
+
tags: #{tags.join(", ")}
|
247
253
|
|
248
254
|
> #{title}
|
249
255
|
EOOUTPUT
|
@@ -251,11 +257,11 @@ module Snibbets
|
|
251
257
|
if langs.count.positive?
|
252
258
|
tags.each do |lang|
|
253
259
|
comment = case lang
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
260
|
+
when /(c|cpp|objectivec|java|javascript|dart|php|golang|typescript|kotlin)/
|
261
|
+
"// Code"
|
262
|
+
else
|
263
|
+
"# Code"
|
264
|
+
end
|
259
265
|
|
260
266
|
output << <<~EOLANGS
|
261
267
|
|
@@ -271,7 +277,7 @@ module Snibbets
|
|
271
277
|
EOCODE
|
272
278
|
end
|
273
279
|
|
274
|
-
File.open(filepath,
|
280
|
+
File.open(filepath, "w") do |f|
|
275
281
|
f.puts output
|
276
282
|
end
|
277
283
|
|
@@ -290,14 +296,14 @@ module Snibbets
|
|
290
296
|
|
291
297
|
if results.empty?
|
292
298
|
out = {
|
293
|
-
|
299
|
+
"title" => "No matching snippets found",
|
294
300
|
}.to_json
|
295
301
|
puts out
|
296
302
|
Process.exit
|
297
303
|
end
|
298
304
|
|
299
305
|
results.each do |result|
|
300
|
-
input = IO.read(result[
|
306
|
+
input = IO.read(result["path"])
|
301
307
|
snippets = input.snippets
|
302
308
|
next if snippets.empty?
|
303
309
|
|
@@ -305,29 +311,29 @@ module Snibbets
|
|
305
311
|
|
306
312
|
if snippets.length == 1
|
307
313
|
output << {
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
314
|
+
"title" => result["title"],
|
315
|
+
"path" => result["path"],
|
316
|
+
"action" => "copyIt",
|
317
|
+
"actionArgument" => snippets[0]["code"],
|
318
|
+
"label" => "Copy",
|
313
319
|
}
|
314
320
|
next
|
315
321
|
end
|
316
322
|
|
317
323
|
snippets.each { |s|
|
318
324
|
children << {
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
325
|
+
"title" => s["title"],
|
326
|
+
"path" => result["path"],
|
327
|
+
"action" => "copyIt",
|
328
|
+
"actionArgument" => s["code"],
|
329
|
+
"label" => "Copy",
|
324
330
|
}
|
325
331
|
}
|
326
332
|
|
327
333
|
output << {
|
328
|
-
|
329
|
-
|
330
|
-
|
334
|
+
"title" => result["title"],
|
335
|
+
"path" => result["path"],
|
336
|
+
"children" => children,
|
331
337
|
}
|
332
338
|
end
|
333
339
|
|
@@ -340,14 +346,14 @@ module Snibbets
|
|
340
346
|
else
|
341
347
|
filepath = nil
|
342
348
|
if results.empty?
|
343
|
-
warn
|
349
|
+
warn "No results" if Snibbets.options[:interactive]
|
344
350
|
Process.exit 0
|
345
351
|
elsif results.length == 1 || !Snibbets.options[:interactive]
|
346
|
-
filepath = results[0][
|
352
|
+
filepath = results[0]["path"]
|
347
353
|
input = IO.read(filepath)
|
348
354
|
else
|
349
|
-
answer = Snibbets::Menu.menu(results, title:
|
350
|
-
filepath = answer[
|
355
|
+
answer = Snibbets::Menu.menu(results, title: "Select a file")
|
356
|
+
filepath = answer["path"]
|
351
357
|
input = IO.read(filepath)
|
352
358
|
end
|
353
359
|
|
@@ -364,21 +370,21 @@ module Snibbets
|
|
364
370
|
snippets = input.snippets
|
365
371
|
|
366
372
|
if snippets.empty?
|
367
|
-
warn
|
373
|
+
warn "No snippets found" if Snibbets.options[:interactive]
|
368
374
|
Process.exit 0
|
369
375
|
elsif snippets.length == 1 || !Snibbets.options[:interactive]
|
370
|
-
if Snibbets.options[:output] ==
|
376
|
+
if Snibbets.options[:output] == "json"
|
371
377
|
print(snippets.to_json, filepath)
|
372
378
|
else
|
373
379
|
snippets.each do |snip|
|
374
|
-
header = File.basename(filepath,
|
380
|
+
header = File.basename(filepath, ".md")
|
375
381
|
if $stdout.isatty
|
376
382
|
puts "{bw}#{header}{x}".x
|
377
|
-
puts "{dw}#{
|
378
|
-
puts
|
383
|
+
puts "{dw}#{"-" * header.length}{x}".x
|
384
|
+
puts ""
|
379
385
|
end
|
380
|
-
code = snip[
|
381
|
-
lang = snip[
|
386
|
+
code = snip["code"]
|
387
|
+
lang = snip["language"]
|
382
388
|
|
383
389
|
print(code, filepath, lang)
|
384
390
|
end
|
@@ -394,49 +400,48 @@ module Snibbets
|
|
394
400
|
end
|
395
401
|
|
396
402
|
def select_snippet(snippets, filepath)
|
397
|
-
snippets.push({
|
398
|
-
answer = Menu.menu(snippets.dup, filename: File.basename(filepath,
|
403
|
+
snippets.push({ "title" => "All snippets", "code" => "" })
|
404
|
+
answer = Menu.menu(snippets.dup, filename: File.basename(filepath, ".md"), title: "Select snippet", query: @query)
|
399
405
|
|
400
|
-
if answer[
|
401
|
-
snippets.delete_if { |s| s[
|
402
|
-
if Snibbets.options[:output] ==
|
406
|
+
if answer["title"] == "All snippets"
|
407
|
+
snippets.delete_if { |s| s["title"] == "All snippets" }
|
408
|
+
if Snibbets.options[:output] == "json"
|
403
409
|
print(snippets.to_json, filepath)
|
404
410
|
else
|
405
411
|
if $stdout.isatty
|
406
|
-
header = File.basename(filepath,
|
412
|
+
header = File.basename(filepath, ".md")
|
407
413
|
warn "{bw}#{header}{x}".x
|
408
|
-
warn "{dw}#{
|
409
|
-
warn
|
414
|
+
warn "{dw}#{"=" * header.length}{x}".x
|
415
|
+
warn ""
|
410
416
|
end
|
411
417
|
print_all(snippets, filepath)
|
412
418
|
end
|
413
|
-
elsif Snibbets.options[:output] ==
|
419
|
+
elsif Snibbets.options[:output] == "json"
|
414
420
|
print(answer.to_json, filepath)
|
415
421
|
else
|
416
422
|
if $stdout.isatty
|
417
|
-
header = "{bw}#{File.basename(filepath,
|
423
|
+
header = "{bw}#{File.basename(filepath, ".md")}: {c}#{answer["title"]}{x}".x
|
418
424
|
warn header
|
419
|
-
warn
|
420
|
-
warn
|
425
|
+
warn "-" * header.length
|
426
|
+
warn ""
|
421
427
|
end
|
422
|
-
code = answer[
|
423
|
-
lang = answer[
|
428
|
+
code = answer["code"]
|
429
|
+
lang = answer["language"]
|
424
430
|
print(code, filepath, lang)
|
425
431
|
end
|
426
432
|
end
|
427
433
|
|
428
434
|
def print_all(snippets, filepath)
|
429
|
-
if Snibbets.options[:output] ==
|
435
|
+
if Snibbets.options[:output] == "json"
|
430
436
|
print(snippets.to_json, filepath)
|
431
437
|
else
|
432
|
-
|
433
438
|
snippets.each do |snippet|
|
434
|
-
lang = snippet[
|
439
|
+
lang = snippet["language"]
|
435
440
|
|
436
|
-
puts "{dw}### {xbw}#{snippet[
|
437
|
-
puts
|
441
|
+
puts "{dw}### {xbw}#{snippet["title"]} {xdw}### {x}".x
|
442
|
+
puts ""
|
438
443
|
|
439
|
-
print(snippet[
|
444
|
+
print(snippet["code"], filepath, lang)
|
440
445
|
puts
|
441
446
|
end
|
442
447
|
end
|
@@ -445,9 +450,9 @@ module Snibbets
|
|
445
450
|
def print(output, filepath, syntax = nil)
|
446
451
|
if Snibbets.options[:copy]
|
447
452
|
OS.copy(Snibbets.options[:all_notes] ? output : output.clean_code)
|
448
|
-
warn
|
453
|
+
warn "Copied to clipboard"
|
449
454
|
end
|
450
|
-
if Snibbets.options[:highlight] && Snibbets.options[:output] ==
|
455
|
+
if Snibbets.options[:highlight] && Snibbets.options[:output] == "raw"
|
451
456
|
$stdout.puts(Highlight.highlight(output, filepath, syntax))
|
452
457
|
else
|
453
458
|
$stdout.puts(Snibbets.options[:all_notes] ? output : output.clean_code)
|
data/mise.toml
ADDED