snibbets 2.0.7
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 +7 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +96 -0
- data/LICENSE.txt +20 -0
- data/README.md +173 -0
- data/README.rdoc +6 -0
- data/Rakefile +87 -0
- data/bin/snibbets +427 -0
- data/buildnotes.md +39 -0
- data/lib/snibbets/config.rb +70 -0
- data/lib/snibbets/hash.rb +17 -0
- data/lib/snibbets/highlight.rb +26 -0
- data/lib/snibbets/lexers.rb +34 -0
- data/lib/snibbets/menu.rb +136 -0
- data/lib/snibbets/os.rb +100 -0
- data/lib/snibbets/string.rb +153 -0
- data/lib/snibbets/version.rb +5 -0
- data/lib/snibbets/which.rb +36 -0
- data/lib/snibbets.rb +32 -0
- data/snibbets.gemspec +44 -0
- metadata +242 -0
data/bin/snibbets
ADDED
@@ -0,0 +1,427 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
5
|
+
require 'snibbets'
|
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
|
+
module Snibbets
|
292
|
+
class << self
|
293
|
+
attr_reader :arguments, :query
|
294
|
+
|
295
|
+
def run
|
296
|
+
options = Snibbets.config.options
|
297
|
+
|
298
|
+
@arguments = {
|
299
|
+
save_config: false,
|
300
|
+
edit_config: false,
|
301
|
+
edit_snippet: false,
|
302
|
+
paste_snippet: false
|
303
|
+
}
|
304
|
+
|
305
|
+
optparse = OptionParser.new do |opts|
|
306
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} [options] query"
|
307
|
+
|
308
|
+
opts.on('-a', '--all', 'If a file contains multiple snippets, output all of them (no menu)') do
|
309
|
+
options[:all] = true
|
310
|
+
end
|
311
|
+
|
312
|
+
opts.on('-c', '--[no-]copy', 'Copy the output to the clibpoard (also displays on STDOUT)') do |v|
|
313
|
+
options[:copy] = v
|
314
|
+
end
|
315
|
+
|
316
|
+
opts.on('-e', '--edit', 'Open the selected snippet in your configured editor') do
|
317
|
+
@arguments[:edit_snippet] = true
|
318
|
+
end
|
319
|
+
|
320
|
+
opts.on('-n', '--[no-]name-only', 'Only search file names, not content') do |v|
|
321
|
+
options[:name_only] = v
|
322
|
+
end
|
323
|
+
|
324
|
+
opts.on('-o', '--output FORMAT', 'Output format (json|launchbar|*raw)') do |outformat|
|
325
|
+
valid = %w[json launchbar lb raw]
|
326
|
+
if outformat.downcase =~ /(launchbar|lb)/
|
327
|
+
options[:launchbar] = true
|
328
|
+
options[:interactive] = false
|
329
|
+
elsif valid.include?(outformat.downcase)
|
330
|
+
options[:output] = outformat.downcase
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
opts.on('-p', '--paste', '--new', 'Interactively create a new snippet from clipboard contents (Mac only)') do
|
335
|
+
@arguments[:paste_snippet] = true
|
336
|
+
end
|
337
|
+
|
338
|
+
opts.on('-q', '--quiet', 'Skip menus and display first match') do
|
339
|
+
options[:interactive] = false
|
340
|
+
options[:launchbar] = false
|
341
|
+
end
|
342
|
+
|
343
|
+
opts.on('-s', '--source FOLDER', 'Snippets folder to search') do |folder|
|
344
|
+
options[:source] = File.expand_path(folder)
|
345
|
+
end
|
346
|
+
|
347
|
+
opts.on('--configure', 'Open the configuration file in your default editor') do
|
348
|
+
@arguments[:edit_config] = true
|
349
|
+
end
|
350
|
+
|
351
|
+
opts.on('--highlight', 'Use pygments or skylighting to syntax highlight (if installed)') do
|
352
|
+
options[:highlight] = true
|
353
|
+
end
|
354
|
+
|
355
|
+
opts.on('--save', 'Save the current command line options to the YAML configuration') do
|
356
|
+
@arguments[:save_config] = true
|
357
|
+
end
|
358
|
+
|
359
|
+
opts.on('-h', '--help', 'Display this screen') do
|
360
|
+
puts "Snibbets v#{VERSION}"
|
361
|
+
puts
|
362
|
+
puts optparse
|
363
|
+
Process.exit 0
|
364
|
+
end
|
365
|
+
|
366
|
+
opts.on('-v', '--version', 'Display version information') do
|
367
|
+
puts "Snibbets v#{VERSION}"
|
368
|
+
Process.exit 0
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
optparse.parse!
|
373
|
+
|
374
|
+
if @arguments[:save_config]
|
375
|
+
config = Snibbets::Config.new
|
376
|
+
config.write_config
|
377
|
+
puts "Configuration saved to #{config.config_file}"
|
378
|
+
end
|
379
|
+
|
380
|
+
if @arguments[:edit_config]
|
381
|
+
config = Snibbets::Config.new
|
382
|
+
config.write_config
|
383
|
+
open_snippet_in_editor(config.config_file)
|
384
|
+
Process.exit 0
|
385
|
+
end
|
386
|
+
|
387
|
+
unless File.directory?(options[:source])
|
388
|
+
puts 'The Snippets folder doesn\'t exist, please configure it.'
|
389
|
+
puts 'Run `snibbets --configure` to open the config file for editing.'
|
390
|
+
Process.exit 1
|
391
|
+
end
|
392
|
+
|
393
|
+
if @arguments[:paste_snippet]
|
394
|
+
Snibbets.new_snippet_from_clipboard
|
395
|
+
Process.exit 0
|
396
|
+
end
|
397
|
+
|
398
|
+
@query = ''
|
399
|
+
|
400
|
+
if options[:launchbar]
|
401
|
+
@query = if $stdin.stat.size.positive?
|
402
|
+
$stdin.read.force_encoding('utf-8')
|
403
|
+
else
|
404
|
+
ARGV.join(' ')
|
405
|
+
end
|
406
|
+
elsif ARGV.length
|
407
|
+
@query = ARGV.join(' ')
|
408
|
+
end
|
409
|
+
|
410
|
+
@query = CGI.unescape(@query)
|
411
|
+
|
412
|
+
if @query.strip.empty?
|
413
|
+
if @arguments[:save_config]
|
414
|
+
Process.exit 0
|
415
|
+
else
|
416
|
+
puts 'No search query'
|
417
|
+
puts optparse
|
418
|
+
Process.exit 1
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
handle_results(search(try: 0))
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
Snibbets.run
|
data/buildnotes.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
template: gem, git, gli, project
|
2
|
+
executable: na
|
3
|
+
readme: README.md
|
4
|
+
changelog: CHANGELOG.md
|
5
|
+
project: snibbets
|
6
|
+
|
7
|
+
# Snibbets
|
8
|
+
|
9
|
+
A plain text snippet manager
|
10
|
+
|
11
|
+
## Test
|
12
|
+
|
13
|
+
@run(bundle exec bin/snibbets $@)
|
14
|
+
|
15
|
+
## Deploy
|
16
|
+
|
17
|
+
You no longer need to manually bump the version, it will be incremented when this task runs.
|
18
|
+
|
19
|
+
```run Update Changelog and README
|
20
|
+
#!/bin/bash
|
21
|
+
|
22
|
+
changelog -u
|
23
|
+
# scripts/fixreadme.rb
|
24
|
+
changelog | git commit -a -F -
|
25
|
+
git pull
|
26
|
+
git push
|
27
|
+
```
|
28
|
+
|
29
|
+
@include(gem:Release Gem) Release Gem
|
30
|
+
@include(project:Update Blog Project) Update Blog Project
|
31
|
+
@run(rake bump[patch]) Bump Version
|
32
|
+
|
33
|
+
@after
|
34
|
+
Don't forget to publish the website!
|
35
|
+
@end
|
36
|
+
|
37
|
+
## Plans
|
38
|
+
|
39
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Snibbets
|
4
|
+
class Config
|
5
|
+
attr_accessor :options
|
6
|
+
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
all: false,
|
9
|
+
copy: false,
|
10
|
+
editor: nil,
|
11
|
+
extension: 'md',
|
12
|
+
highlight: false,
|
13
|
+
interactive: true,
|
14
|
+
launchbar: false,
|
15
|
+
name_only: false,
|
16
|
+
output: 'raw',
|
17
|
+
source: File.expand_path('~/Dropbox/Snippets')
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
custom_config = read_config
|
22
|
+
@options = DEFAULT_OPTIONS.merge(custom_config)
|
23
|
+
@options[:editor] ||= best_editor
|
24
|
+
write_config unless @options.equal?(custom_config)
|
25
|
+
end
|
26
|
+
|
27
|
+
def best_editor
|
28
|
+
if ENV['EDITOR']
|
29
|
+
ENV['EDITOR']
|
30
|
+
elsif ENV['GIT_EDITOR']
|
31
|
+
ENV['GIT_EDITOR']
|
32
|
+
else
|
33
|
+
return TTY::Which.which('code') if TTY::Which.exist?('code')
|
34
|
+
|
35
|
+
return TTY::Which.which('subl') if TTY::Which.exist?('subl')
|
36
|
+
|
37
|
+
return TTY::Which.which('bbedit') if TTY::Which.exist?('bbedit')
|
38
|
+
|
39
|
+
return TTY::Which.which('nano') if TTY::Which.exist?('nano')
|
40
|
+
|
41
|
+
return TTY::Which.which('vim') if TTY::Which.exist?('vim')
|
42
|
+
|
43
|
+
'TextEdit'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def config_dir
|
48
|
+
File.expand_path('~/.config/snibbets')
|
49
|
+
end
|
50
|
+
|
51
|
+
def config_file
|
52
|
+
File.join(config_dir, 'snibbets.yml')
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_config
|
56
|
+
if File.exist?(config_file)
|
57
|
+
YAML.safe_load(IO.read(config_file)).symbolize_keys
|
58
|
+
else
|
59
|
+
{}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def write_config
|
64
|
+
raise 'Error creating config directory, file exists' if File.exist?(config_dir) && !File.directory?(config_dir)
|
65
|
+
|
66
|
+
FileUtils.mkdir_p(config_dir) unless File.directory?(config_dir)
|
67
|
+
File.open(config_file, 'w') { |f| f.puts(YAML.dump(@options.stringify_keys)) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Snibbets
|
4
|
+
class ::Hash
|
5
|
+
# Turn all keys into string
|
6
|
+
#
|
7
|
+
# Return a copy of the hash where all its keys are strings
|
8
|
+
def stringify_keys
|
9
|
+
each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v.is_a?(Hash) ? v.stringify_keys : v }
|
10
|
+
end
|
11
|
+
|
12
|
+
# Turn all keys into symbols
|
13
|
+
def symbolize_keys
|
14
|
+
each_with_object({}) { |(k, v), hsh| hsh[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Snibbets
|
2
|
+
class Highlight
|
3
|
+
def highlight_pygments(executable, code, syntax, theme)
|
4
|
+
syntax = syntax.empty? ? '-g' : "-l #{syntax}"
|
5
|
+
`echo #{Shellwords.escape(code)} | #{executable} #{syntax}`
|
6
|
+
end
|
7
|
+
|
8
|
+
def highlight_skylight(executable, code, syntax, theme)
|
9
|
+
return code if syntax.empty?
|
10
|
+
|
11
|
+
`echo #{Shellwords.escape(code)} | #{executable} --syntax #{syntax}`
|
12
|
+
end
|
13
|
+
|
14
|
+
def highlight(code, filename, theme = 'monokai')
|
15
|
+
syntax = syntax_from_extension(filename)
|
16
|
+
|
17
|
+
skylight = TTY::Which.which('skylighting')
|
18
|
+
return highlight_skylight(skylight, code, syntax, theme) unless skylight.empty?
|
19
|
+
|
20
|
+
pygments = TTY::Which.which('pygmentize')
|
21
|
+
return highlight_pygments(pygments, code, syntax, theme) unless pygments.empty?
|
22
|
+
|
23
|
+
code
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Snibbets
|
2
|
+
class Lexers
|
3
|
+
attr_accessor :lexers
|
4
|
+
|
5
|
+
def build_lexers
|
6
|
+
IO.read('lexers_db.txt').split(/\n/).each do |line|
|
7
|
+
key = line.match(/(?mi)^((, )?[^,]+?)+?(?=\[)/)[0]
|
8
|
+
keys = key.split(/,/).map(&:strip)
|
9
|
+
value = line.match(/\[(.*?)\]/)[1]
|
10
|
+
values = value.split(/,/).map(&:strip)
|
11
|
+
|
12
|
+
@lexers << {
|
13
|
+
lexer: keys.shift,
|
14
|
+
aliases: keys,
|
15
|
+
extensions: values
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def ext_to_lang(ext)
|
21
|
+
matches = @lexers.select { |lex| lex[:extensions].map(&:downcase).include?(ext.downcase) }
|
22
|
+
matches.map { |lex| lex[:lexer] }.first
|
23
|
+
end
|
24
|
+
|
25
|
+
def lang_to_ext(lexer)
|
26
|
+
matches = @lexers.select { |lex| lex[:lexer] == lexer || lex[:aliases].map(&:downcase).include?(lexer.downcase) }
|
27
|
+
matches.map { |lex| lex[:extensions].first }.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def syntax_from_extension(filename)
|
31
|
+
ext_to_lang(filename.split(/\./)[1])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|