summaryrefslogtreecommitdiff
path: root/bin/coderay
blob: 3dea52829a9dc83c7eaf84ee65b26191767131b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/usr/bin/env ruby
require 'coderay'

$options, args = ARGV.partition { |arg| arg[/^-[hv]$|--\w+/] }
subcommand = args.first if /^\w/ === args.first
subcommand = nil if subcommand && File.exist?(subcommand)
args.delete subcommand

def option? *options
  !($options & options).empty?
end

def tty?
  $stdout.tty? || option?('--tty')
end

def version
  puts <<-USAGE
CodeRay #{CodeRay::VERSION}
  USAGE
end

def help
  puts <<-HELP
This is CodeRay #{CodeRay::VERSION}, a syntax highlighting tool for selected languages.

usage:
  coderay [-language] [input] [-format] [output]
  
defaults:
  language   detect from input file name or shebang; fall back to plain text
  input      STDIN
  format     detect from output file name or use terminal; fall back to HTML
  output     STDOUT

common:
  coderay file.rb                      # highlight file to terminal
  coderay file.rb > file.html          # highlight file to HTML page
  coderay file.rb -div > file.html     # highlight file to HTML snippet

configure output:
  coderay file.py output.json          # output tokens as JSON
  coderay file.py -loc                 # count lines of code in Python file

configure input:
  coderay -python file                 # specify the input language
  coderay -ruby                        # take input from STDIN

more:
  coderay stylesheet [style]           # print CSS stylesheet
  HELP
end

def commands
  puts <<-COMMANDS
  general:
    highlight   code highlighting (default command, optional)
    stylesheet  print the CSS stylesheet with the given name (aliases: style, css)
  
  about:
    list [of]   list all available plugins (or just the scanners|encoders|styles|filetypes)
    commands    print this list
    help        show some help
    version     print CodeRay version
  COMMANDS
end

def print_list_of plugin_host
  plugins = plugin_host.all_plugins.map do |plugin|
    info = "  #{plugin.plugin_id}: #{plugin.title}"
    
    aliases = (plugin.aliases - [:default]).map { |key| "-#{key}" }.sort_by { |key| key.size }
    if plugin.respond_to?(:file_extension) || !aliases.empty?
      additional_info = []
      additional_info << aliases.join(', ') unless aliases.empty?
      info << " (#{additional_info.join('; ')})"
    end
    
    info << '  <-- default' if plugin.aliases.include? :default
    
    info
  end
  puts plugins.sort
end

if option? '-v', '--version'
  version
end

if option? '-h', '--help'
  help
end

case subcommand
when 'highlight', nil
  if ARGV.empty?
    version
    help
  else
    signature = args.map { |arg| arg[/^-/] ? '-' : 'f' }.join
    names     = args.map { |arg| arg.sub(/^-/, '') }
    case signature
    when /^$/
      exit
    when /^ff?$/
      input_file, output_file, = *names
    when /^f-f?$/
      input_file, output_format, output_file, = *names
    when /^-ff?$/
      input_lang, input_file, output_file, = *names
    when /^-f-f?$/
      input_lang, input_file, output_format, output_file, = *names
    when /^--?f?$/
      input_lang, output_format, output_file, = *names
    else
      $stdout = $stderr
      help
      puts
      puts "Unknown parameter order: #{args.join ' '}, expected: [-language] [input] [-format] [output]"
      exit 1
    end
    
    if input_file
      input_lang ||= CodeRay::FileType.fetch input_file, :text, true
    end
    
    if output_file
      output_format ||= CodeRay::FileType[output_file] || :plain
    else
      output_format ||= :terminal_256
    end
    
    output_format = :page if output_format.to_s == 'html'
    
    if input_file
      input = File.read input_file
    else
      input = $stdin.read
    end
    
    begin
      file =
        if output_file
          File.open output_file, 'w'
        else
          $stdout
        end
      CodeRay.encode(input, input_lang, output_format, :out => file)
      file.puts
    rescue CodeRay::PluginHost::PluginNotFound => boom
      $stdout = $stderr
      if boom.message[/CodeRay::(\w+)s could not load plugin :?(.*?): /]
        puts "I don't know the #$1 \"#$2\"."
      else
        puts boom.message
      end
      # puts "I don't know this plugin: #{boom.message[/Could not load plugin (.*?): /, 1]}."
    rescue CodeRay::Scanners::Scanner::ScanError  # FIXME: rescue Errno::EPIPE
      # this is sometimes raised by pagers; ignore [TODO: wtf?]
    ensure
      file.close if output_file
    end
  end
when 'li', 'list'
  arg = args.first && args.first.downcase
  if [nil, 's', 'sc', 'scanner', 'scanners'].include? arg
    puts 'input languages (Scanners):'
    print_list_of CodeRay::Scanners
  end
  
  if [nil, 'e', 'en', 'enc', 'encoder', 'encoders'].include? arg
    puts 'output formats (Encoders):'
    print_list_of CodeRay::Encoders
  end
  
  if [nil, 'st', 'style', 'styles'].include? arg
    puts 'CSS themes for HTML output (Styles):'
    print_list_of CodeRay::Styles
  end
  
  if [nil, 'f', 'ft', 'file', 'filetype', 'filetypes'].include? arg
    puts 'recognized file types:'
    
    filetypes = Hash.new { |h, k| h[k] = [] }
    CodeRay::FileType::TypeFromExt.inject filetypes do |types, (ext, type)|
      types[type.to_s] << ".#{ext}"
      types
    end
    CodeRay::FileType::TypeFromName.inject filetypes do |types, (name, type)|
      types[type.to_s] << name
      types
    end
    
    filetypes.sort.each do |type, exts|
      puts "  #{type}: #{exts.sort_by { |ext| ext.size }.join(', ')}"
    end
  end
when 'stylesheet', 'style', 'css'
  puts CodeRay::Encoders[:html]::CSS.new(args.first || :default).stylesheet
when 'commands'
  commands
when 'help'
  help
else
  $stdout = $stderr
  help
  puts
  if subcommand[/\A\w+\z/]
    puts "Unknown command: #{subcommand}"
  else
    puts "File not found: #{subcommand}"
  end
  exit 1
end