Chris@909: #!/usr/bin/env ruby Chris@909: require 'coderay' Chris@909: Chris@909: $options, args = ARGV.partition { |arg| arg[/^-[hv]$|--\w+/] } Chris@909: subcommand = args.first if /^\w/ === args.first Chris@909: subcommand = nil if subcommand && File.exist?(subcommand) Chris@909: args.delete subcommand Chris@909: Chris@909: def option? *options Chris@909: !($options & options).empty? Chris@909: end Chris@909: Chris@909: def tty? Chris@909: $stdout.tty? || option?('--tty') Chris@909: end Chris@909: Chris@909: def version Chris@909: puts <<-USAGE Chris@909: CodeRay #{CodeRay::VERSION} Chris@909: USAGE Chris@909: end Chris@909: Chris@909: def help Chris@909: puts <<-HELP Chris@909: This is CodeRay #{CodeRay::VERSION}, a syntax highlighting tool for selected languages. Chris@909: Chris@909: usage: Chris@909: coderay [-language] [input] [-format] [output] Chris@909: Chris@909: defaults: Chris@909: language detect from input file name or shebang; fall back to plain text Chris@909: input STDIN Chris@909: format detect from output file name or use terminal; fall back to HTML Chris@909: output STDOUT Chris@909: Chris@909: common: Chris@909: coderay file.rb # highlight file to terminal Chris@909: coderay file.rb > file.html # highlight file to HTML page Chris@909: coderay file.rb -div > file.html # highlight file to HTML snippet Chris@909: Chris@909: configure output: Chris@909: coderay file.py output.json # output tokens as JSON Chris@909: coderay file.py -loc # count lines of code in Python file Chris@909: Chris@909: configure input: Chris@909: coderay -python file # specify the input language Chris@909: coderay -ruby # take input from STDIN Chris@909: Chris@909: more: Chris@909: coderay stylesheet [style] # print CSS stylesheet Chris@909: HELP Chris@909: end Chris@909: Chris@909: def commands Chris@909: puts <<-COMMANDS Chris@909: general: Chris@909: highlight code highlighting (default command, optional) Chris@909: stylesheet print the CSS stylesheet with the given name (aliases: style, css) Chris@909: Chris@909: about: Chris@909: list [of] list all available plugins (or just the scanners|encoders|styles|filetypes) Chris@909: commands print this list Chris@909: help show some help Chris@909: version print CodeRay version Chris@909: COMMANDS Chris@909: end Chris@909: Chris@909: def print_list_of plugin_host Chris@909: plugins = plugin_host.all_plugins.map do |plugin| Chris@909: info = " #{plugin.plugin_id}: #{plugin.title}" Chris@909: Chris@909: aliases = (plugin.aliases - [:default]).map { |key| "-#{key}" }.sort_by { |key| key.size } Chris@909: if plugin.respond_to?(:file_extension) || !aliases.empty? Chris@909: additional_info = [] Chris@909: additional_info << aliases.join(', ') unless aliases.empty? Chris@909: info << " (#{additional_info.join('; ')})" Chris@909: end Chris@909: Chris@909: info << ' <-- default' if plugin.aliases.include? :default Chris@909: Chris@909: info Chris@909: end Chris@909: puts plugins.sort Chris@909: end Chris@909: Chris@909: if option? '-v', '--version' Chris@909: version Chris@909: end Chris@909: Chris@909: if option? '-h', '--help' Chris@909: help Chris@909: end Chris@909: Chris@909: case subcommand Chris@909: when 'highlight', nil Chris@909: if ARGV.empty? Chris@909: version Chris@909: help Chris@909: else Chris@909: signature = args.map { |arg| arg[/^-/] ? '-' : 'f' }.join Chris@909: names = args.map { |arg| arg.sub(/^-/, '') } Chris@909: case signature Chris@909: when /^$/ Chris@909: exit Chris@909: when /^ff?$/ Chris@909: input_file, output_file, = *names Chris@909: when /^f-f?$/ Chris@909: input_file, output_format, output_file, = *names Chris@909: when /^-ff?$/ Chris@909: input_lang, input_file, output_file, = *names Chris@909: when /^-f-f?$/ Chris@909: input_lang, input_file, output_format, output_file, = *names Chris@909: when /^--?f?$/ Chris@909: input_lang, output_format, output_file, = *names Chris@909: else Chris@909: $stdout = $stderr Chris@909: help Chris@909: puts Chris@909: puts "Unknown parameter order: #{args.join ' '}, expected: [-language] [input] [-format] [output]" Chris@909: exit 1 Chris@909: end Chris@909: Chris@909: if input_file Chris@909: input_lang ||= CodeRay::FileType.fetch input_file, :text, true Chris@909: end Chris@909: Chris@909: if output_file Chris@909: output_format ||= CodeRay::FileType[output_file] Chris@909: else Chris@909: output_format ||= :terminal Chris@909: end Chris@909: Chris@909: output_format = :page if output_format.to_s == 'html' Chris@909: Chris@909: if input_file Chris@909: input = File.read input_file Chris@909: else Chris@909: input = $stdin.read Chris@909: end Chris@909: Chris@909: begin Chris@909: file = Chris@909: if output_file Chris@909: File.open output_file, 'w' Chris@909: else Chris@909: $stdout.sync = true Chris@909: $stdout Chris@909: end Chris@909: CodeRay.encode(input, input_lang, output_format, :out => file) Chris@909: file.puts Chris@909: rescue CodeRay::PluginHost::PluginNotFound => boom Chris@909: $stdout = $stderr Chris@909: if boom.message[/CodeRay::(\w+)s could not load plugin :?(.*?): /] Chris@909: puts "I don't know the #$1 \"#$2\"." Chris@909: else Chris@909: puts boom.message Chris@909: end Chris@909: # puts "I don't know this plugin: #{boom.message[/Could not load plugin (.*?): /, 1]}." Chris@909: rescue CodeRay::Scanners::Scanner::ScanError # FIXME: rescue Errno::EPIPE Chris@909: # this is sometimes raised by pagers; ignore [TODO: wtf?] Chris@909: ensure Chris@909: file.close if output_file Chris@909: end Chris@909: end Chris@909: when 'li', 'list' Chris@909: arg = args.first && args.first.downcase Chris@909: if [nil, 's', 'sc', 'scanner', 'scanners'].include? arg Chris@909: puts 'input languages (Scanners):' Chris@909: print_list_of CodeRay::Scanners Chris@909: end Chris@909: Chris@909: if [nil, 'e', 'en', 'enc', 'encoder', 'encoders'].include? arg Chris@909: puts 'output formats (Encoders):' Chris@909: print_list_of CodeRay::Encoders Chris@909: end Chris@909: Chris@909: if [nil, 'st', 'style', 'styles'].include? arg Chris@909: puts 'CSS themes for HTML output (Styles):' Chris@909: print_list_of CodeRay::Styles Chris@909: end Chris@909: Chris@909: if [nil, 'f', 'ft', 'file', 'filetype', 'filetypes'].include? arg Chris@909: puts 'recognized file types:' Chris@909: Chris@909: filetypes = Hash.new { |h, k| h[k] = [] } Chris@909: CodeRay::FileType::TypeFromExt.inject filetypes do |types, (ext, type)| Chris@909: types[type.to_s] << ".#{ext}" Chris@909: types Chris@909: end Chris@909: CodeRay::FileType::TypeFromName.inject filetypes do |types, (name, type)| Chris@909: types[type.to_s] << name Chris@909: types Chris@909: end Chris@909: Chris@909: filetypes.sort.each do |type, exts| Chris@909: puts " #{type}: #{exts.sort_by { |ext| ext.size }.join(', ')}" Chris@909: end Chris@909: end Chris@909: when 'stylesheet', 'style', 'css' Chris@909: puts CodeRay::Encoders[:html]::CSS.new(args.first).stylesheet Chris@909: when 'commands' Chris@909: commands Chris@909: when 'help' Chris@909: help Chris@909: else Chris@909: $stdout = $stderr Chris@909: help Chris@909: puts Chris@909: if subcommand[/\A\w+\z/] Chris@909: puts "Unknown command: #{subcommand}" Chris@909: else Chris@909: puts "File not found: #{subcommand}" Chris@909: end Chris@909: exit 1 Chris@909: end