lib/snibbets.rb in snibbets-2.0.7 vs lib/snibbets.rb in snibbets-2.0.9

- old
+ new

@@ -11,10 +11,11 @@ require_relative 'snibbets/version' require_relative 'snibbets/config' require_relative 'snibbets/which' require_relative 'snibbets/string' require_relative 'snibbets/hash' +require_relative 'snibbets/array' require_relative 'snibbets/menu' require_relative 'snibbets/os' require_relative 'snibbets/highlight' require_relative 'snibbets/lexers' @@ -25,8 +26,292 @@ @config ||= Config.new end def options @options = config.options + end + end +end + +module Snibbets + class << self + # Search the snippets directory for query using find and grep + def search(try: 0) + folder = Snibbets.options[:source] + # start by doing a spotlight search, if that fails, start trying: + # First try only search by filenames + # Second try search with grep + ext = Snibbets.options[:extension] || 'md' + cmd = case try + when 1 + %(find "#{folder}" -iregex '#{@query.rx}' -name '*.#{ext}') + when 2 + rg = TTY::Which.which('rg') + ag = TTY::Which.which('ag') + ack = TTY::Which.which('ack') + grep = TTY::Which.which('grep') + if Snibbets.options[:name_only] + nil + elsif !rg.empty? + %(#{rg} -li --color=never --glob='*.#{ext}' '#{@query.rx}' "#{folder}") + elsif !ag.empty? + %(#{ag} -li --nocolor -G '.*.#{ext}' '#{@query.rx}' "#{folder}") + elsif !ack.empty? + %(#{ack} -li --nocolor --markdown '#{@query.rx}' "#{folder}") + elsif !grep.empty? + %(#{grep} -iEl '#{@query.rx}' "#{folder}"/**/*.#{ext}) + else + nil + end + else + mdfind = TTY::Which.which('mdfind') + if mdfind.empty? + nil + else + name_only = Snibbets.options[:name_only] ? '-name ' : '' + %(mdfind -onlyin #{folder} #{name_only}'#{@query} filename:.#{ext}' 2>/dev/null) + end + end + + if try == 2 && cmd.nil? + puts "No search method available on this system. Please install ripgrep, silver surfer, ack, or grep." + Process.exit 1 + end + + res = cmd.nil? ? '' : `#{cmd}`.strip + + matches = [] + + unless res.empty? + lines = res.split(/\n/) + lines.each do |l| + matches << { + 'title' => File.basename(l, '.*'), + 'path' => l + } + end + + matches.sort_by! { |a| a['title'] }.uniq! + + return matches unless matches.empty? + end + + return matches if try == 2 + + # if no results on the first try, try again searching all text + search(try: try + 1) if matches.empty? + end + + def open_snippet_in_editor(filepath) + editor = Snibbets.options[:editor] || Snibbets::Config.best_editor + + os = RbConfig::CONFIG['target_os'] + + if editor.nil? + OS.open(filepath) + else + if os =~ /darwin.*/i + if editor =~ /^TextEdit/ + `open -a TextEdit "#{filepath}"` + elsif TTY::Which.bundle_id?(editor) + `open -b "#{editor}" "#{filepath}"` + elsif TTY::Which.app?(editor) + `open -a "#{editor}" "#{filepath}"` + elsif TTY::Which.exist?(editor) + editor = TTY::Which.which(editor) + system %(#{editor} "#{filepath}") if editor + else + puts "No editor configured, or editor is missing" + Process.exit 1 + end + elsif TTY::Which.exist?(editor) + editor = TTY::Which.which(editor) + system %(#{editor} "#{filepath}") if editor + else + puts "No editor configured, or editor is missing" + Process.exit 1 + end + end + end + + def new_snippet_from_clipboard + trap('SIGINT') do + Howzit.console.info "\nCancelled" + exit! + end + + build_lexers + + pb = OS.paste.outdent + + printf 'What does this snippet do? ' + input = $stdin.gets.chomp + title = input unless input.empty? + + printf 'What language(s) does it use (separate with spaces, full names or file extensions will work)? ' + input = $stdin.gets.chomp + langs = input.split(/ +/).map(&:strip) unless input.empty? + exts = langs.map { |lang| Snibbets::Lexers.lang_to_ext(lang) } + tags = langs.map { |lang| Snibbets::Lexers.ext_to_lang(lang) }.concat(langs).sort.uniq + + filename ="#{title}.#{exts.join('.')}.#{Snibbets.options[:extension]}" + + File.open(File.join(Snibbets.options[:source], filename), 'w') do |f| + f.puts "tags: #{tags.join(', ')} + + ``` + #{pb} + ```" + end + + puts "New snippet written to #{filename}." + end + + def handle_launchbar(results) + output = [] + + if results.empty? + out = { + 'title' => 'No matching snippets found' + }.to_json + puts out + Process.exit + end + + results.each do |result| + input = IO.read(result['path']) + snippets = input.snippets + next if snippets.empty? + + children = [] + + if snippets.length == 1 + output << { + 'title' => result['title'], + 'path' => result['path'], + 'action' => 'copyIt', + 'actionArgument' => snippets[0]['code'], + 'label' => 'Copy' + } + next + end + + snippets.each { |s| + children << { + 'title' => s['title'], + 'path' => result['path'], + 'action' => 'copyIt', + 'actionArgument' => s['code'], + 'label' => 'Copy' + } + } + + output << { + 'title' => result['title'], + 'path' => result['path'], + 'children' => children + } + end + + puts output.to_json + end + + def handle_results(results) + if Snibbets.options[:launchbar] + handle_launchbar(results) + else + filepath = nil + if results.empty? + warn 'No results' + Process.exit 0 + elsif results.length == 1 || !Snibbets.options[:interactive] + filepath = results[0]['path'] + input = IO.read(filepath) + else + answer = Snibbets::Menu.menu(results, title: 'Select a file') + filepath = answer['path'] + input = IO.read(filepath) + end + + if @arguments[:edit_snippet] + open_snippet_in_editor(filepath) + Process.exit 0 + end + + snippets = input.snippets + + if snippets.empty? + warn 'No snippets found' + Process.exit 0 + elsif snippets.length == 1 || !Snibbets.options[:interactive] + if Snibbets.options[:output] == 'json' + print(snippets.to_json) + else + snippets.each do |snip| + header = File.basename(filepath, '.md') + warn header + warn '-' * header.length + code = snip['code'] + code = highlight(code, filepath) if Snibbets.options[:highlight] + print(code) + end + end + elsif snippets.length > 1 + if Snibbets.options[:all] + if Snibbets.options[:output] == 'json' + print(snippets.to_json) + else + output = [] + snippets.each do |snippet| + output << snippet['title'] + output << '-' * snippet['title'].length + output << snippet['code'] + output << "\n" + end + print(output.join("\n")) + end + else + snippets.push({ 'title' => 'All snippets', 'code' => '' }) + + answer = Snibbets::Menu.menu(snippets, filename: File.basename(filepath, '.md'), title: 'Select snippet', query: @query) + + if answer['title'] == 'All snippets' + snippets.delete_if { |s| s['title'] == 'All snippets'} + if Snibbets.options[:output] == 'json' + print(snippets.to_json) + else + header = File.basename(filepath, '.md') + warn header + warn '=' * header.length + output = [] + snippets.each do |snippet| + output << snippet['title'] + output << '-' * snippet['title'].length + output << snippet['code'] + output << "\n" + end + print(output.join("\n")) + end + elsif Snibbets.options[:output] == 'json' + print(answer.to_json) + else + header = "#{File.basename(filepath, '.md')}: #{answer['title']}" + warn header + warn '-' * header.length + code = answer['code'] + code = highlight(code, filepath) if Snibbets.options[:highlight] + print(code) + end + end + end + end + end + + def print(output) + $stdout.puts(output) + if Snibbets.options[:copy] + OS.copy(output) + $stderr.puts "Copied to clipboard" + end end end end