snibbets 2.0.7 → 2.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4b5be01b0a7d1768bef21a63ffd7c1f1f176a77d6d3fa1be5ff84db7b0b27eb
4
- data.tar.gz: 7c65f52ecb3dacbeea62c8225f9dc70db7d90d358a4c284050b7149a9b19fe75
3
+ metadata.gz: f07f0d4772b8d611c0e29da125b4e6915d3909479c4384348699eedc2e1baf90
4
+ data.tar.gz: e9abdf59d66f3c099c0c25f20a912efc53b690c7c45a5f4ada2b18355c3d473c
5
5
  SHA512:
6
- metadata.gz: e4adffb0b196b8f891ac9363fdcb91a67dccfc579530568e336dd1ced2d7d8f6c7f4496fa1c4cbe992e24655a37a7808bd49c48e44d75623a2c1fa0fb1615bb9
7
- data.tar.gz: 796ec5249c2c7332132ac2138d5281c05aab7259b75726319f247499543a15c4b839d6929e764850a8bbc907747a42a179d3b61625e4b4fff7ca90f698c24509
6
+ metadata.gz: ac873395a3735c827164f03b497f21f06e8af227b515e299c40eb8a4eeff26c907a5893077e1ee1a528200ef4ef9e6fe7c5ac1c9ddfecf7522ed0d772e6fd6eb
7
+ data.tar.gz: ca65c353589adda9fb4af59d390e98e277e5ba1523d746ab3f5cce19119cfa541b2db36045a90ecddd73be7224665dd96dd8642a2d7c010c438e6e9141a79d6a
data/CHANGELOG.md CHANGED
@@ -1,6 +1,30 @@
1
- ### 2.0.7
1
+ ### 2.0.9
2
2
 
3
- 2023-04-15 12:02
3
+ 2023-04-15 15:44
4
+
5
+ #### IMPROVED
6
+
7
+ - Allow setting `menus` config key to force Snibbets to use fzf, gum, or console menus
8
+ - Allow setting `menus` config key to force Snibbets to use fzf, gum, or console menus
9
+
10
+ #### FIXED
11
+
12
+ - If a header section contains no code blocks, don't display it in menu
13
+ - Remove leading and trailing newlines without affecting indentation
14
+
15
+ ### 2.0.8
16
+
17
+ 2023-04-15 15:41
18
+
19
+ #### IMPROVED
20
+
21
+ - Allow setting `menus` config key to force Snibbets to use fzf, gum, or console menus
22
+ - Allow setting `menus` config key to force Snibbets to use fzf, gum, or console menus
23
+
24
+ #### FIXED
25
+
26
+ - If a header section contains no code blocks, don't display it in menu
27
+ - Remove leading and trailing newlines without affecting indentation
4
28
 
5
29
  ### 2.0.6
6
30
 
data/CHANGELOG.md.orig ADDED
@@ -0,0 +1,56 @@
1
+ ### 2.0.10
2
+
3
+ 2023-04-15 16:28
4
+
5
+ #### FIXED
6
+
7
+ - Update dependencies for security
8
+ - Incorporate fixes from @robjwells addressing #3 and #4
9
+ - Incorporate fixes from @robjwells addressing #3 and #4
10
+ - Best menu CLI determination missing modules
11
+
12
+ <<<<<<< HEAD
13
+ ### 2.0.7
14
+
15
+ 2023-04-15 12:02
16
+ =======
17
+ ### 2.0.9
18
+
19
+ 2023-04-15 15:44
20
+
21
+ #### IMPROVED
22
+
23
+ - Allow setting `menus` config key to force Snibbets to use fzf, gum, or console menus
24
+ - Allow setting `menus` config key to force Snibbets to use fzf, gum, or console menus
25
+
26
+ #### FIXED
27
+
28
+ - If a header section contains no code blocks, don't display it in menu
29
+ - Remove leading and trailing newlines without affecting indentation
30
+
31
+ ### 2.0.8
32
+
33
+ 2023-04-15 15:41
34
+
35
+ #### IMPROVED
36
+
37
+ - Allow setting `menus` config key to force Snibbets to use fzf, gum, or console menus
38
+ - Allow setting `menus` config key to force Snibbets to use fzf, gum, or console menus
39
+
40
+ #### FIXED
41
+
42
+ - If a header section contains no code blocks, don't display it in menu
43
+ - Remove leading and trailing newlines without affecting indentation
44
+ >>>>>>> release/2.0.9
45
+
46
+ ### 2.0.6
47
+
48
+ 2023-04-15 11:55
49
+
50
+ #### IMPROVED
51
+
52
+ - Refactor script as modules and classes
53
+
54
+ ## 2.0.2
55
+
56
+ Initial release as a gem
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- snibbets (2.0.3)
4
+ snibbets (2.0.10)
5
5
  tty-which (~> 0.5, >= 0.5.0)
6
6
 
7
7
  GEM
@@ -9,6 +9,7 @@ GEM
9
9
  specs:
10
10
  ansi (1.5.0)
11
11
  ast (2.4.2)
12
+ awesome_print (1.9.2)
12
13
  diff-lcs (1.5.0)
13
14
  docile (1.4.0)
14
15
  gem-release (2.2.2)
@@ -25,7 +26,7 @@ GEM
25
26
  ast (~> 2.4.1)
26
27
  rainbow (3.1.1)
27
28
  rake (13.0.6)
28
- rdoc (4.3.0)
29
+ rdoc (6.3.3)
29
30
  regexp_parser (2.7.0)
30
31
  rexml (3.2.5)
31
32
  rspec (3.12.0)
@@ -76,21 +77,25 @@ GEM
76
77
  thor (1.2.1)
77
78
  tty-which (0.5.0)
78
79
  unicode-display_width (2.4.2)
80
+ yard (0.9.34)
79
81
 
80
82
  PLATFORMS
81
83
  arm64-darwin-20
84
+ x86_64-linux
82
85
 
83
86
  DEPENDENCIES
87
+ awesome_print (~> 1.9)
84
88
  bundler (~> 2.0)
85
89
  gem-release (~> 2.2)
86
90
  parse_gemspec-cli (~> 1.0)
87
91
  rake (~> 13.0)
88
- rdoc (~> 4.3)
92
+ rdoc (~> 6.3.1)
89
93
  rspec (~> 3.0)
90
94
  simplecov (~> 0.21)
91
95
  simplecov-console (~> 0.9)
92
96
  snibbets!
93
97
  standard (~> 1.3)
98
+ yard (~> 0.9, >= 0.9.26)
94
99
 
95
100
  BUNDLED WITH
96
101
  2.2.29
data/Gemfile.lock.orig ADDED
@@ -0,0 +1,107 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ snibbets (2.0.10)
5
+ tty-which (~> 0.5, >= 0.5.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ansi (1.5.0)
11
+ ast (2.4.2)
12
+ awesome_print (1.9.2)
13
+ diff-lcs (1.5.0)
14
+ docile (1.4.0)
15
+ gem-release (2.2.2)
16
+ json (2.6.3)
17
+ language_server-protocol (3.17.0.3)
18
+ multi_json (1.15.0)
19
+ parallel (1.22.1)
20
+ parse_gemspec (1.0.0)
21
+ parse_gemspec-cli (1.0.0)
22
+ multi_json
23
+ parse_gemspec
24
+ thor
25
+ parser (3.2.2.0)
26
+ ast (~> 2.4.1)
27
+ rainbow (3.1.1)
28
+ rake (13.0.6)
29
+ <<<<<<< HEAD
30
+ rdoc (6.3.1)
31
+ =======
32
+ rdoc (6.3.3)
33
+ >>>>>>> af6f234 (Update rdoc)
34
+ regexp_parser (2.7.0)
35
+ rexml (3.2.5)
36
+ rspec (3.12.0)
37
+ rspec-core (~> 3.12.0)
38
+ rspec-expectations (~> 3.12.0)
39
+ rspec-mocks (~> 3.12.0)
40
+ rspec-core (3.12.1)
41
+ rspec-support (~> 3.12.0)
42
+ rspec-expectations (3.12.2)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.12.0)
45
+ rspec-mocks (3.12.5)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (~> 3.12.0)
48
+ rspec-support (3.12.0)
49
+ rubocop (1.48.1)
50
+ json (~> 2.3)
51
+ parallel (~> 1.10)
52
+ parser (>= 3.2.0.0)
53
+ rainbow (>= 2.2.2, < 4.0)
54
+ regexp_parser (>= 1.8, < 3.0)
55
+ rexml (>= 3.2.5, < 4.0)
56
+ rubocop-ast (>= 1.26.0, < 2.0)
57
+ ruby-progressbar (~> 1.7)
58
+ unicode-display_width (>= 2.4.0, < 3.0)
59
+ rubocop-ast (1.28.0)
60
+ parser (>= 3.2.1.0)
61
+ rubocop-performance (1.16.0)
62
+ rubocop (>= 1.7.0, < 2.0)
63
+ rubocop-ast (>= 0.4.0)
64
+ ruby-progressbar (1.13.0)
65
+ simplecov (0.22.0)
66
+ docile (~> 1.1)
67
+ simplecov-html (~> 0.11)
68
+ simplecov_json_formatter (~> 0.1)
69
+ simplecov-console (0.9.1)
70
+ ansi
71
+ simplecov
72
+ terminal-table
73
+ simplecov-html (0.12.3)
74
+ simplecov_json_formatter (0.1.4)
75
+ standard (1.26.0)
76
+ language_server-protocol (~> 3.17.0.2)
77
+ rubocop (~> 1.48.1)
78
+ rubocop-performance (~> 1.16.0)
79
+ terminal-table (3.0.2)
80
+ unicode-display_width (>= 1.1.1, < 3)
81
+ thor (1.2.1)
82
+ tty-which (0.5.0)
83
+ unicode-display_width (2.4.2)
84
+ webrick (1.7.0)
85
+ yard (0.9.28)
86
+ webrick (~> 1.7.0)
87
+
88
+ PLATFORMS
89
+ arm64-darwin-20
90
+ x86_64-linux
91
+
92
+ DEPENDENCIES
93
+ awesome_print (~> 1.9)
94
+ bundler (~> 2.0)
95
+ gem-release (~> 2.2)
96
+ parse_gemspec-cli (~> 1.0)
97
+ rake (~> 13.0)
98
+ rdoc (~> 6.3.1)
99
+ rspec (~> 3.0)
100
+ simplecov (~> 0.21)
101
+ simplecov-console (~> 0.9)
102
+ snibbets!
103
+ standard (~> 1.3)
104
+ yard (~> 0.9, >= 0.9.26)
105
+
106
+ BUNDLED WITH
107
+ 2.2.29
data/README.md CHANGED
@@ -88,6 +88,7 @@ extension: md
88
88
  highlight: false
89
89
  interactive: true
90
90
  launchbar: false
91
+ menus:
91
92
  name_only: false
92
93
  output: raw
93
94
  source: "~/Dropbox/Snippets"
@@ -111,6 +112,8 @@ The `highlight` key turns on syntax highlighting. This requires that either `pyg
111
112
 
112
113
  The `interactive` setting determines whether menus will be displayed. This should generally be true, but if you want silent operation that just displays the best match automatically, set it to false.
113
114
 
115
+ The `menus` setting will determine what method is used for displaying interactive menus. If this is not set, it will be automatically determined in the order of `fzf`, `gum`, and `console`. You can manually choose to use one of these options over another by making it the `menus` setting.
116
+
114
117
  The `name_only` key will permanently set Snibbets to only search for snippets by their filename rather than examining their contents. You can enable this at runtime using `--name-only` in the command.
115
118
 
116
119
  ### Usage
data/bin/snibbets CHANGED
@@ -4,290 +4,6 @@
4
4
  $LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
5
5
  require 'snibbets'
6
6
 
7
- module Snibbets
8
- class << self
9
- # Search the snippets directory for query using find and grep
10
- def search(try: 0)
11
- folder = Snibbets.options[:source]
12
- # start by doing a spotlight search, if that fails, start trying:
13
- # First try only search by filenames
14
- # Second try search with grep
15
- ext = Snibbets.options[:extension] || 'md'
16
- cmd = case try
17
- when 1
18
- %(find "#{folder}" -iregex '#{@query.rx}' -name '*.#{ext}')
19
- when 2
20
- rg = TTY::Which.which('rg')
21
- ag = TTY::Which.which('ag')
22
- ack = TTY::Which.which('ack')
23
- grep = TTY::Which.which('grep')
24
- if Snibbets.options[:name_only]
25
- nil
26
- elsif !rg.empty?
27
- %(#{rg} -li --color=never --glob='*.#{ext}' '#{@query.rx}' "#{folder}")
28
- elsif !ag.empty?
29
- %(#{ag} -li --nocolor -G '.*.#{ext}' '#{@query.rx}' "#{folder}")
30
- elsif !ack.empty?
31
- %(#{ack} -li --nocolor --markdown '#{@query.rx}' "#{folder}")
32
- elsif !grep.empty?
33
- %(#{grep} -iEl '#{@query.rx}' "#{folder}"/**/*.#{ext})
34
- else
35
- nil
36
- end
37
- else
38
- mdfind = TTY::Which.which('mdfind')
39
- if mdfind.empty?
40
- nil
41
- else
42
- name_only = Snibbets.options[:name_only] ? '-name ' : ''
43
- %(mdfind -onlyin #{folder} #{name_only}'#{@query} filename:.#{ext}' 2>/dev/null)
44
- end
45
- end
46
-
47
- if try == 2 && cmd.nil?
48
- puts "No search method available on this system. Please install ripgrep, silver surfer, ack, or grep."
49
- Process.exit 1
50
- end
51
-
52
- res = cmd.nil? ? '' : `#{cmd}`.strip
53
-
54
- matches = []
55
-
56
- unless res.empty?
57
- lines = res.split(/\n/)
58
- lines.each do |l|
59
- matches << {
60
- 'title' => File.basename(l, '.*'),
61
- 'path' => l
62
- }
63
- end
64
-
65
- matches.sort_by! { |a| a['title'] }.uniq!
66
-
67
- return matches unless matches.empty?
68
- end
69
-
70
- return matches if try == 2
71
-
72
- # if no results on the first try, try again searching all text
73
- search(try: try + 1) if matches.empty?
74
- end
75
-
76
- def open_snippet_in_editor(filepath)
77
- editor = Snibbets.options[:editor] || Snibbets::Config.best_editor
78
-
79
- os = RbConfig::CONFIG['target_os']
80
-
81
- if editor.nil?
82
- OS.open(filepath)
83
- else
84
- if os =~ /darwin.*/i
85
- if editor =~ /^TextEdit/
86
- `open -a TextEdit "#{filepath}"`
87
- elsif TTY::Which.bundle_id?(editor)
88
- `open -b "#{editor}" "#{filepath}"`
89
- elsif TTY::Which.app?(editor)
90
- `open -a "#{editor}" "#{filepath}"`
91
- elsif TTY::Which.exist?(editor)
92
- editor = TTY::Which.which(editor)
93
- system %(#{editor} "#{filepath}") if editor
94
- else
95
- puts "No editor configured, or editor is missing"
96
- Process.exit 1
97
- end
98
- elsif TTY::Which.exist?(editor)
99
- editor = TTY::Which.which(editor)
100
- system %(#{editor} "#{filepath}") if editor
101
- else
102
- puts "No editor configured, or editor is missing"
103
- Process.exit 1
104
- end
105
- end
106
- end
107
-
108
- def new_snippet_from_clipboard
109
- trap('SIGINT') do
110
- Howzit.console.info "\nCancelled"
111
- exit!
112
- end
113
-
114
- build_lexers
115
-
116
- pb = OS.paste.outdent
117
-
118
- printf 'What does this snippet do? '
119
- input = $stdin.gets.chomp
120
- title = input unless input.empty?
121
-
122
- printf 'What language(s) does it use (separate with spaces, full names or file extensions will work)? '
123
- input = $stdin.gets.chomp
124
- langs = input.split(/ +/).map(&:strip) unless input.empty?
125
- exts = langs.map { |lang| Snibbets::Lexers.lang_to_ext(lang) }
126
- tags = langs.map { |lang| Snibbets::Lexers.ext_to_lang(lang) }.concat(langs).sort.uniq
127
-
128
- filename ="#{title}.#{exts.join('.')}.#{Snibbets.options[:extension]}"
129
-
130
- File.open(File.join(Snibbets.options[:source], filename), 'w') do |f|
131
- f.puts "tags: #{tags.join(', ')}
132
-
133
- ```
134
- #{pb}
135
- ```"
136
- end
137
-
138
- puts "New snippet written to #{filename}."
139
- end
140
-
141
- def handle_launchbar(results)
142
- output = []
143
-
144
- if results.empty?
145
- out = {
146
- 'title' => 'No matching snippets found'
147
- }.to_json
148
- puts out
149
- Process.exit
150
- end
151
-
152
- results.each do |result|
153
- input = IO.read(result['path'])
154
- snippets = input.snippets
155
- next if snippets.empty?
156
-
157
- children = []
158
-
159
- if snippets.length == 1
160
- output << {
161
- 'title' => result['title'],
162
- 'path' => result['path'],
163
- 'action' => 'copyIt',
164
- 'actionArgument' => snippets[0]['code'],
165
- 'label' => 'Copy'
166
- }
167
- next
168
- end
169
-
170
- snippets.each { |s|
171
- children << {
172
- 'title' => s['title'],
173
- 'path' => result['path'],
174
- 'action' => 'copyIt',
175
- 'actionArgument' => s['code'],
176
- 'label' => 'Copy'
177
- }
178
- }
179
-
180
- output << {
181
- 'title' => result['title'],
182
- 'path' => result['path'],
183
- 'children' => children
184
- }
185
- end
186
-
187
- puts output.to_json
188
- end
189
-
190
- def handle_results(results)
191
- if Snibbets.options[:launchbar]
192
- handle_launchbar(results)
193
- else
194
- filepath = nil
195
- if results.empty?
196
- warn 'No results'
197
- Process.exit 0
198
- elsif results.length == 1 || !Snibbets.options[:interactive]
199
- filepath = results[0]['path']
200
- input = IO.read(filepath)
201
- else
202
- answer = Snibbets::Menu.menu(results, title: 'Select a file')
203
- filepath = answer['path']
204
- input = IO.read(filepath)
205
- end
206
-
207
- if @arguments[:edit_snippet]
208
- open_snippet_in_editor(filepath)
209
- Process.exit 0
210
- end
211
-
212
- snippets = input.snippets
213
-
214
- if snippets.empty?
215
- warn 'No snippets found'
216
- Process.exit 0
217
- elsif snippets.length == 1 || !Snibbets.options[:interactive]
218
- if Snibbets.options[:output] == 'json'
219
- print(snippets.to_json)
220
- else
221
- snippets.each do |snip|
222
- header = File.basename(filepath, '.md')
223
- warn header
224
- warn '-' * header.length
225
- code = snip['code']
226
- code = highlight(code, filepath) if Snibbets.options[:highlight]
227
- print(code)
228
- end
229
- end
230
- elsif snippets.length > 1
231
- if Snibbets.options[:all]
232
- if Snibbets.options[:output] == 'json'
233
- print(snippets.to_json)
234
- else
235
- output = []
236
- snippets.each do |snippet|
237
- output << snippet['title']
238
- output << '-' * snippet['title'].length
239
- output << snippet['code']
240
- output << "\n"
241
- end
242
- print(output.join("\n"))
243
- end
244
- else
245
- snippets.push({ 'title' => 'All snippets', 'code' => '' })
246
-
247
- answer = Snibbets::Menu.menu(snippets, filename: File.basename(filepath, '.md'), title: 'Select snippet', query: @query)
248
-
249
- if answer['title'] == 'All snippets'
250
- snippets.delete_if { |s| s['title'] == 'All snippets'}
251
- if Snibbets.options[:output] == 'json'
252
- print(snippets.to_json)
253
- else
254
- header = File.basename(filepath, '.md')
255
- warn header
256
- warn '=' * header.length
257
- output = []
258
- snippets.each do |snippet|
259
- output << snippet['title']
260
- output << '-' * snippet['title'].length
261
- output << snippet['code']
262
- output << "\n"
263
- end
264
- print(output.join("\n"))
265
- end
266
- elsif Snibbets.options[:output] == 'json'
267
- print(answer.to_json)
268
- else
269
- header = "#{File.basename(filepath, '.md')}: #{answer['title']}"
270
- warn header
271
- warn '-' * header.length
272
- code = answer['code']
273
- code = highlight(code, filepath) if Snibbets.options[:highlight]
274
- print(code)
275
- end
276
- end
277
- end
278
- end
279
- end
280
-
281
- def print(output)
282
- $stdout.puts(output)
283
- if Snibbets.options[:copy]
284
- OS.copy(output)
285
- $stderr.puts "Copied to clipboard"
286
- end
287
- end
288
- end
289
- end
290
-
291
7
  module Snibbets
292
8
  class << self
293
9
  attr_reader :arguments, :query
data/buildnotes.md CHANGED
@@ -20,7 +20,6 @@ You no longer need to manually bump the version, it will be incremented when thi
20
20
  #!/bin/bash
21
21
 
22
22
  changelog -u
23
- # scripts/fixreadme.rb
24
23
  changelog | git commit -a -F -
25
24
  git pull
26
25
  git push
@@ -0,0 +1,39 @@
1
+ module Snibbets
2
+ class ::Array
3
+ def blocks
4
+ select { |el| el =~ /^<block\d+>$/ }.count
5
+ end
6
+
7
+ def strip_empty
8
+ remove_leading_empty_elements.remove_trailing_empty_elements
9
+ end
10
+
11
+ def strip_empty!
12
+ replace strip_empty
13
+ end
14
+
15
+ def remove_leading_empty_elements
16
+ output = []
17
+
18
+ each do |line|
19
+ next if line =~ /^\s*$/ || line.empty?
20
+
21
+ output << line
22
+ end
23
+
24
+ output
25
+ end
26
+
27
+ def remove_trailing_empty_elements
28
+ output = []
29
+
30
+ reverse.each do |line|
31
+ next if line =~ /^\s*$/ || line.empty?
32
+
33
+ output << line
34
+ end
35
+
36
+ output.reverse
37
+ end
38
+ end
39
+ end
@@ -12,6 +12,7 @@ module Snibbets
12
12
  highlight: false,
13
13
  interactive: true,
14
14
  launchbar: false,
15
+ menus: nil,
15
16
  name_only: false,
16
17
  output: 'raw',
17
18
  source: File.expand_path('~/Dropbox/Snippets')
@@ -21,9 +22,17 @@ module Snibbets
21
22
  custom_config = read_config
22
23
  @options = DEFAULT_OPTIONS.merge(custom_config)
23
24
  @options[:editor] ||= best_editor
25
+ @options[:menus] ||= best_menu
26
+
24
27
  write_config unless @options.equal?(custom_config)
25
28
  end
26
29
 
30
+ def best_menu
31
+ return 'fzf' if TTY::Which.exist?('fzf')
32
+ return 'gum' if TTY::Which.exist?('gum')
33
+ 'console'
34
+ end
35
+
27
36
  def best_editor
28
37
  if ENV['EDITOR']
29
38
  ENV['EDITOR']
data/lib/snibbets/menu.rb CHANGED
@@ -113,7 +113,7 @@ module Snibbets
113
113
  return res[line - 1] if line.positive? && line <= res.length
114
114
 
115
115
  warn 'Out of range'
116
- console_menu(res, title)
116
+ return console_menu(res, title, filename, query: query)
117
117
  end
118
118
  rescue Interrupt
119
119
  system('stty', stty_save)
@@ -122,11 +122,23 @@ module Snibbets
122
122
  end
123
123
 
124
124
  def menu(res, filename: nil, title: 'Select one', query: nil)
125
+ menu_opt = Snibbets.options[:menus]
125
126
  query&.remove_spotlight_tags!
127
+
126
128
  fzf = TTY::Which.which('fzf')
129
+ gum = TTY::Which.which('gum')
130
+
131
+ case menu_opt
132
+ when /fzf/
133
+ return fzf_menu(fzf, res, title, query, filename) unless fzf.empty?
134
+ when /gum/
135
+ return gum_menu(gum, res, title, query, filename) unless gum.empty?
136
+ when /console/
137
+ return console_menu(res, title, filename, query: query)
138
+ end
139
+
127
140
  return fzf_menu(fzf, res, title, query, filename) unless fzf.empty?
128
141
 
129
- gum = TTY::Which.which('gum')
130
142
  return gum_menu(gum, res, title, query, filename) unless gum.empty?
131
143
 
132
144
  console_menu(res, title, filename, query: query)
@@ -28,6 +28,10 @@ module Snibbets
28
28
  lines.join("\n")
29
29
  end
30
30
 
31
+ def strip_newlines
32
+ split(/\n/).strip_empty.join("\n")
33
+ end
34
+
31
35
  # Are there multiple snippets (indicated by ATX headers)
32
36
  def multiple?
33
37
  gsub(/(`{3,}).*?\n\1/m, '').scan(/^#+/).length > 1
@@ -131,8 +135,10 @@ module Snibbets
131
135
 
132
136
  parts.shift if parts.count > 1
133
137
 
134
- parts.each do |p|
135
- lines = p.split(/\n/)
138
+ parts.each do |part|
139
+ lines = part.split(/\n/).strip_empty
140
+
141
+ next if lines.blocks == 0
136
142
 
137
143
  title = lines.count > 1 ? lines.shift.strip.sub(/[.:]$/, '') : 'Default snippet'
138
144
  block = lines.join("\n").gsub(/<(block\d+)>/) { code_blocks[Regexp.last_match(1)] }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Snibbets
4
- VERSION = '2.0.7'
4
+ VERSION = '2.0.10'
5
5
  end
data/lib/snibbets.rb CHANGED
@@ -13,6 +13,7 @@ require_relative 'snibbets/config'
13
13
  require_relative 'snibbets/which'
14
14
  require_relative 'snibbets/string'
15
15
  require_relative 'snibbets/hash'
16
+ require_relative 'snibbets/array'
16
17
  require_relative 'snibbets/menu'
17
18
  require_relative 'snibbets/os'
18
19
  require_relative 'snibbets/highlight'
@@ -30,3 +31,287 @@ module Snibbets
30
31
  end
31
32
  end
32
33
  end
34
+
35
+ module Snibbets
36
+ class << self
37
+ # Search the snippets directory for query using find and grep
38
+ def search(try: 0)
39
+ folder = File.expand_path(Snibbets.options[:source])
40
+ # start by doing a spotlight search, if that fails, start trying:
41
+ # First try only search by filenames
42
+ # Second try search with grep
43
+ ext = Snibbets.options[:extension] || 'md'
44
+ cmd = case try
45
+ when 1
46
+ %(find "#{folder}" -iregex '^#{Regexp.escape(folder)}/#{@query.rx}' -name '*.#{ext}')
47
+ when 2
48
+ rg = TTY::Which.which('rg')
49
+ ag = TTY::Which.which('ag')
50
+ ack = TTY::Which.which('ack')
51
+ grep = TTY::Which.which('grep')
52
+ if Snibbets.options[:name_only]
53
+ nil
54
+ elsif !rg.empty?
55
+ %(#{rg} -li --color=never --glob='*.#{ext}' '#{@query.rx}' "#{folder}")
56
+ elsif !ag.empty?
57
+ %(#{ag} -li --nocolor -G '.*.#{ext}' '#{@query.rx}' "#{folder}")
58
+ elsif !ack.empty?
59
+ %(#{ack} -li --nocolor --markdown '#{@query.rx}' "#{folder}")
60
+ elsif !grep.empty?
61
+ %(#{grep} -iEl '#{@query.rx}' "#{folder}"/**/*.#{ext})
62
+ else
63
+ nil
64
+ end
65
+ else
66
+ mdfind = TTY::Which.which('mdfind')
67
+ if mdfind.empty?
68
+ nil
69
+ else
70
+ name_only = Snibbets.options[:name_only] ? '-name ' : ''
71
+ %(mdfind -onlyin #{folder} #{name_only}'#{@query} filename:.#{ext}' 2>/dev/null)
72
+ end
73
+ end
74
+
75
+ if try == 2 && cmd.nil?
76
+ puts "No search method available on this system. Please install ripgrep, silver surfer, ack, or grep."
77
+ Process.exit 1
78
+ end
79
+
80
+ res = cmd.nil? ? '' : `#{cmd}`.strip
81
+
82
+ matches = []
83
+
84
+ unless res.empty?
85
+ lines = res.split(/\n/)
86
+ lines.each do |l|
87
+ matches << {
88
+ 'title' => File.basename(l, '.*'),
89
+ 'path' => l
90
+ }
91
+ end
92
+
93
+ matches.sort_by! { |a| a['title'] }.uniq!
94
+
95
+ return matches unless matches.empty?
96
+ end
97
+
98
+ return matches if try == 2
99
+
100
+ # if no results on the first try, try again searching all text
101
+ search(try: try + 1) if matches.empty?
102
+ end
103
+
104
+ def open_snippet_in_editor(filepath)
105
+ editor = Snibbets.options[:editor] || Snibbets::Config.best_editor
106
+
107
+ os = RbConfig::CONFIG['target_os']
108
+
109
+ if editor.nil?
110
+ OS.open(filepath)
111
+ else
112
+ if os =~ /darwin.*/i
113
+ if editor =~ /^TextEdit/
114
+ `open -a TextEdit "#{filepath}"`
115
+ elsif TTY::Which.bundle_id?(editor)
116
+ `open -b "#{editor}" "#{filepath}"`
117
+ elsif TTY::Which.app?(editor)
118
+ `open -a "#{editor}" "#{filepath}"`
119
+ elsif TTY::Which.exist?(editor)
120
+ editor = TTY::Which.which(editor)
121
+ system %(#{editor} "#{filepath}") if editor
122
+ else
123
+ puts "No editor configured, or editor is missing"
124
+ Process.exit 1
125
+ end
126
+ elsif TTY::Which.exist?(editor)
127
+ editor = TTY::Which.which(editor)
128
+ system %(#{editor} "#{filepath}") if editor
129
+ else
130
+ puts "No editor configured, or editor is missing"
131
+ Process.exit 1
132
+ end
133
+ end
134
+ end
135
+
136
+ def new_snippet_from_clipboard
137
+ trap('SIGINT') do
138
+ Howzit.console.info "\nCancelled"
139
+ exit!
140
+ end
141
+
142
+ build_lexers
143
+
144
+ pb = OS.paste.outdent
145
+
146
+ printf 'What does this snippet do? '
147
+ input = $stdin.gets.chomp
148
+ title = input unless input.empty?
149
+
150
+ printf 'What language(s) does it use (separate with spaces, full names or file extensions will work)? '
151
+ input = $stdin.gets.chomp
152
+ langs = input.split(/ +/).map(&:strip) unless input.empty?
153
+ exts = langs.map { |lang| Snibbets::Lexers.lang_to_ext(lang) }
154
+ tags = langs.map { |lang| Snibbets::Lexers.ext_to_lang(lang) }.concat(langs).sort.uniq
155
+
156
+ filename ="#{title}.#{exts.join('.')}.#{Snibbets.options[:extension]}"
157
+
158
+ File.open(File.join(File.expand_path(Snibbets.options[:source]), filename), 'w') do |f|
159
+ f.puts "tags: #{tags.join(', ')}
160
+
161
+ ```
162
+ #{pb}
163
+ ```"
164
+ end
165
+
166
+ puts "New snippet written to #{filename}."
167
+ end
168
+
169
+ def handle_launchbar(results)
170
+ output = []
171
+
172
+ if results.empty?
173
+ out = {
174
+ 'title' => 'No matching snippets found'
175
+ }.to_json
176
+ puts out
177
+ Process.exit
178
+ end
179
+
180
+ results.each do |result|
181
+ input = IO.read(result['path'])
182
+ snippets = input.snippets
183
+ next if snippets.empty?
184
+
185
+ children = []
186
+
187
+ if snippets.length == 1
188
+ output << {
189
+ 'title' => result['title'],
190
+ 'path' => result['path'],
191
+ 'action' => 'copyIt',
192
+ 'actionArgument' => snippets[0]['code'],
193
+ 'label' => 'Copy'
194
+ }
195
+ next
196
+ end
197
+
198
+ snippets.each { |s|
199
+ children << {
200
+ 'title' => s['title'],
201
+ 'path' => result['path'],
202
+ 'action' => 'copyIt',
203
+ 'actionArgument' => s['code'],
204
+ 'label' => 'Copy'
205
+ }
206
+ }
207
+
208
+ output << {
209
+ 'title' => result['title'],
210
+ 'path' => result['path'],
211
+ 'children' => children
212
+ }
213
+ end
214
+
215
+ puts output.to_json
216
+ end
217
+
218
+ def handle_results(results)
219
+ if Snibbets.options[:launchbar]
220
+ handle_launchbar(results)
221
+ else
222
+ filepath = nil
223
+ if results.empty?
224
+ warn 'No results'
225
+ Process.exit 0
226
+ elsif results.length == 1 || !Snibbets.options[:interactive]
227
+ filepath = results[0]['path']
228
+ input = IO.read(filepath)
229
+ else
230
+ answer = Snibbets::Menu.menu(results, title: 'Select a file')
231
+ filepath = answer['path']
232
+ input = IO.read(filepath)
233
+ end
234
+
235
+ if @arguments[:edit_snippet]
236
+ open_snippet_in_editor(filepath)
237
+ Process.exit 0
238
+ end
239
+
240
+ snippets = input.snippets
241
+
242
+ if snippets.empty?
243
+ warn 'No snippets found'
244
+ Process.exit 0
245
+ elsif snippets.length == 1 || !Snibbets.options[:interactive]
246
+ if Snibbets.options[:output] == 'json'
247
+ print(snippets.to_json)
248
+ else
249
+ snippets.each do |snip|
250
+ header = File.basename(filepath, '.md')
251
+ warn header
252
+ warn '-' * header.length
253
+ code = snip['code']
254
+ code = highlight(code, filepath) if Snibbets.options[:highlight]
255
+ print(code)
256
+ end
257
+ end
258
+ elsif snippets.length > 1
259
+ if Snibbets.options[:all]
260
+ if Snibbets.options[:output] == 'json'
261
+ print(snippets.to_json)
262
+ else
263
+ output = []
264
+ snippets.each do |snippet|
265
+ output << snippet['title']
266
+ output << '-' * snippet['title'].length
267
+ output << snippet['code']
268
+ output << "\n"
269
+ end
270
+ print(output.join("\n"))
271
+ end
272
+ else
273
+ snippets.push({ 'title' => 'All snippets', 'code' => '' })
274
+
275
+ answer = Snibbets::Menu.menu(snippets, filename: File.basename(filepath, '.md'), title: 'Select snippet', query: @query)
276
+
277
+ if answer['title'] == 'All snippets'
278
+ snippets.delete_if { |s| s['title'] == 'All snippets'}
279
+ if Snibbets.options[:output] == 'json'
280
+ print(snippets.to_json)
281
+ else
282
+ header = File.basename(filepath, '.md')
283
+ warn header
284
+ warn '=' * header.length
285
+ output = []
286
+ snippets.each do |snippet|
287
+ output << snippet['title']
288
+ output << '-' * snippet['title'].length
289
+ output << snippet['code']
290
+ output << "\n"
291
+ end
292
+ print(output.join("\n"))
293
+ end
294
+ elsif Snibbets.options[:output] == 'json'
295
+ print(answer.to_json)
296
+ else
297
+ header = "#{File.basename(filepath, '.md')}: #{answer['title']}"
298
+ warn header
299
+ warn '-' * header.length
300
+ code = answer['code']
301
+ code = highlight(code, filepath) if Snibbets.options[:highlight]
302
+ print(code)
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ def print(output)
310
+ $stdout.puts(output)
311
+ if Snibbets.options[:copy]
312
+ OS.copy(output)
313
+ $stderr.puts "Copied to clipboard"
314
+ end
315
+ end
316
+ end
317
+ end
data/snibbets.gemspec CHANGED
@@ -31,10 +31,11 @@ Gem::Specification.new do |spec|
31
31
  spec.files += Dir["[A-Z]*"]
32
32
 
33
33
  spec.add_development_dependency "bundler", "~> 2.0"
34
+ spec.add_development_dependency "awesome_print", "~> 1.9"
34
35
  spec.add_development_dependency "gem-release", "~> 2.2"
35
36
  spec.add_development_dependency "parse_gemspec-cli", "~> 1.0"
36
37
  spec.add_development_dependency "rake", "~> 13.0"
37
- spec.add_development_dependency 'rdoc', '~> 4.3'
38
+ spec.add_development_dependency 'rdoc', '~> 6.3.1'
38
39
  spec.add_development_dependency 'yard', '~> 0.9', '>= 0.9.26'
39
40
  spec.add_development_dependency "rspec", "~> 3.0"
40
41
  spec.add_development_dependency "simplecov", "~> 0.21"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snibbets
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.7
4
+ version: 2.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: awesome_print
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.9'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: gem-release
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +86,14 @@ dependencies:
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '4.3'
89
+ version: 6.3.1
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '4.3'
96
+ version: 6.3.1
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: yard
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -185,8 +199,10 @@ extra_rdoc_files:
185
199
  - README.md
186
200
  files:
187
201
  - CHANGELOG.md
202
+ - CHANGELOG.md.orig
188
203
  - Gemfile
189
204
  - Gemfile.lock
205
+ - Gemfile.lock.orig
190
206
  - LICENSE.txt
191
207
  - README.md
192
208
  - README.rdoc
@@ -194,6 +210,7 @@ files:
194
210
  - bin/snibbets
195
211
  - buildnotes.md
196
212
  - lib/snibbets.rb
213
+ - lib/snibbets/array.rb
197
214
  - lib/snibbets/config.rb
198
215
  - lib/snibbets/hash.rb
199
216
  - lib/snibbets/highlight.rb