Chris@0: # encoders/term.rb Chris@0: # By Rob Aldred (http://robaldred.co.uk) Chris@0: # Based on idea by Nathan Weizenbaum (http://nex-3.com) Chris@0: # MIT License (http://www.opensource.org/licenses/mit-license.php) Chris@0: # Chris@0: # A CodeRay encoder that outputs code highlighted for a color terminal. Chris@0: # Check out http://robaldred.co.uk Chris@0: Chris@0: module CodeRay Chris@0: module Encoders Chris@0: class Term < Encoder Chris@0: register_for :term Chris@0: Chris@0: TOKEN_COLORS = { Chris@0: :annotation => '35', Chris@0: :attribute_name => '33', Chris@0: :attribute_name_fat => '33', Chris@0: :attribute_value => '31', Chris@0: :attribute_value_fat => '31', Chris@0: :bin => '1;35', Chris@0: :char => {:self => '36', :delimiter => '34'}, Chris@0: :class => '1;35', Chris@0: :class_variable => '36', Chris@0: :color => '32', Chris@0: :comment => '37', Chris@0: :complex => '34', Chris@0: :constant => ['34', '4'], Chris@0: :decoration => '35', Chris@0: :definition => '1;32', Chris@0: :directive => ['32', '4'], Chris@0: :doc => '46', Chris@0: :doctype => '1;30', Chris@0: :doc_string => ['31', '4'], Chris@0: :entity => '33', Chris@0: :error => ['1;33', '41'], Chris@0: :exception => '1;31', Chris@0: :float => '1;35', Chris@0: :function => '1;34', Chris@0: :global_variable => '42', Chris@0: :hex => '1;36', Chris@0: :important => '1;31', Chris@0: :include => '33', Chris@0: :integer => '1;34', Chris@0: :interpreted => '1;35', Chris@0: :key => '35', Chris@0: :label => '1;4', Chris@0: :local_variable => '33', Chris@0: :oct => '1;35', Chris@0: :operator_name => '1;29', Chris@0: :pre_constant => '1;36', Chris@0: :pre_type => '1;30', Chris@0: :predefined => ['4', '1;34'], Chris@0: :preprocessor => '36', Chris@0: :pseudo_class => '34', Chris@0: :regexp => { Chris@0: :content => '31', Chris@0: :delimiter => '1;29', Chris@0: :modifier => '35', Chris@0: :function => '1;29' Chris@0: }, Chris@0: :reserved => '1;31', Chris@0: :shell => { Chris@0: :self => '42', Chris@0: :content => '1;29', Chris@0: :delimiter => '37', Chris@0: }, Chris@0: :string => { Chris@0: :self => '32', Chris@0: :modifier => '1;32', Chris@0: :escape => '1;36', Chris@0: :delimiter => '1;32', Chris@0: }, Chris@0: :symbol => '1;32', Chris@0: :tag => '34', Chris@0: :tag_fat => '1;34', Chris@0: :tag_special => ['34', '4'], Chris@0: :type => '1;34', Chris@0: :value => '36', Chris@0: :variable => '34', Chris@0: :insert => '42', Chris@0: :delete => '41', Chris@0: :change => '44', Chris@0: :head => '45', Chris@0: } Chris@0: TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved] Chris@0: TOKEN_COLORS[:method] = TOKEN_COLORS[:function] Chris@0: TOKEN_COLORS[:imaginary] = TOKEN_COLORS[:complex] Chris@0: TOKEN_COLORS[:open] = TOKEN_COLORS[:close] = TOKEN_COLORS[:nesting_delimiter] = TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter] Chris@0: Chris@0: protected Chris@0: Chris@0: def setup(options) Chris@0: @out = '' Chris@0: @opened = [nil] Chris@0: @subcolors = nil Chris@0: end Chris@0: Chris@0: def finish(options) Chris@0: super Chris@0: end Chris@0: Chris@0: def token text, type = :plain Chris@0: case text Chris@0: Chris@0: when nil Chris@0: # raise 'Token with nil as text was given: %p' % [[text, type]] Chris@0: Chris@0: when String Chris@0: Chris@0: if color = (@subcolors || TOKEN_COLORS)[type] Chris@0: color = color[:self] || return if Hash === color Chris@0: Chris@0: @out << col(color) + text.gsub("\n", col(0) + "\n" + col(color)) + col(0) Chris@0: @out << col(@subcolors[:self]) if @subcolors && @subcolors[:self] Chris@0: else Chris@0: @out << text Chris@0: end Chris@0: Chris@0: # token groups, eg. strings Chris@0: when :open Chris@0: @opened[0] = type Chris@0: if color = TOKEN_COLORS[type] Chris@0: if Hash === color Chris@0: @subcolors = color Chris@0: @out << col(color[:self]) if color[:self] Chris@0: else Chris@0: @subcolors = {} Chris@0: @out << col(color) Chris@0: end Chris@0: end Chris@0: @opened << type Chris@0: when :close Chris@0: if @opened.empty? Chris@0: # nothing to close Chris@0: else Chris@0: @out << col(0) if (@subcolors || {})[:self] Chris@0: @subcolors = nil Chris@0: @opened.pop Chris@0: end Chris@0: Chris@0: # whole lines to be highlighted, eg. a added/modified/deleted lines in a diff Chris@0: when :begin_line Chris@0: Chris@0: when :end_line Chris@0: Chris@0: else Chris@0: raise 'unknown token kind: %p' % [text] Chris@0: end Chris@0: end Chris@0: Chris@0: private Chris@0: Chris@0: def col(color) Chris@0: Array(color).map { |c| "\e[#{c}m" }.join Chris@0: end Chris@0: end Chris@0: end Chris@0: end