Chris@909: module CodeRay Chris@909: module Encoders Chris@909: Chris@909: # Outputs code highlighted for a color terminal. Chris@909: # Chris@909: # Note: This encoder is in beta. It currently doesn't use the Styles. Chris@909: # Chris@909: # Alias: +term+ Chris@909: # Chris@909: # == Authors & License Chris@909: # Chris@909: # By Rob Aldred (http://robaldred.co.uk) Chris@909: # Chris@909: # Based on idea by Nathan Weizenbaum (http://nex-3.com) Chris@909: # Chris@909: # MIT License (http://www.opensource.org/licenses/mit-license.php) Chris@909: class Terminal < Encoder Chris@909: Chris@909: register_for :terminal Chris@909: Chris@909: TOKEN_COLORS = { Chris@909: :annotation => '35', Chris@909: :attribute_name => '33', Chris@909: :attribute_value => '31', Chris@909: :binary => '1;35', Chris@909: :char => { Chris@909: :self => '36', :delimiter => '34' Chris@909: }, Chris@909: :class => '1;35', Chris@909: :class_variable => '36', Chris@909: :color => '32', Chris@909: :comment => '37', Chris@909: :complex => '34', Chris@909: :constant => ['34', '4'], Chris@909: :decoration => '35', Chris@909: :definition => '1;32', Chris@909: :directive => ['32', '4'], Chris@909: :doc => '46', Chris@909: :doctype => '1;30', Chris@909: :doc_string => ['31', '4'], Chris@909: :entity => '33', Chris@909: :error => ['1;33', '41'], Chris@909: :exception => '1;31', Chris@909: :float => '1;35', Chris@909: :function => '1;34', Chris@909: :global_variable => '42', Chris@909: :hex => '1;36', Chris@909: :include => '33', Chris@909: :integer => '1;34', Chris@909: :key => '35', Chris@909: :label => '1;15', Chris@909: :local_variable => '33', Chris@909: :octal => '1;35', Chris@909: :operator_name => '1;29', Chris@909: :predefined_constant => '1;36', Chris@909: :predefined_type => '1;30', Chris@909: :predefined => ['4', '1;34'], Chris@909: :preprocessor => '36', Chris@909: :pseudo_class => '34', Chris@909: :regexp => { Chris@909: :self => '31', Chris@909: :content => '31', Chris@909: :delimiter => '1;29', Chris@909: :modifier => '35', Chris@909: :function => '1;29' Chris@909: }, Chris@909: :reserved => '1;31', Chris@909: :shell => { Chris@909: :self => '42', Chris@909: :content => '1;29', Chris@909: :delimiter => '37', Chris@909: }, Chris@909: :string => { Chris@909: :self => '32', Chris@909: :modifier => '1;32', Chris@909: :escape => '1;36', Chris@909: :delimiter => '1;32', Chris@909: }, Chris@909: :symbol => '1;32', Chris@909: :tag => '34', Chris@909: :type => '1;34', Chris@909: :value => '36', Chris@909: :variable => '34', Chris@909: Chris@909: :insert => '42', Chris@909: :delete => '41', Chris@909: :change => '44', Chris@909: :head => '45' Chris@909: } Chris@909: TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved] Chris@909: TOKEN_COLORS[:method] = TOKEN_COLORS[:function] Chris@909: TOKEN_COLORS[:imaginary] = TOKEN_COLORS[:complex] Chris@909: TOKEN_COLORS[:begin_group] = TOKEN_COLORS[:end_group] = Chris@909: TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter] Chris@909: Chris@909: protected Chris@909: Chris@909: def setup(options) Chris@909: super Chris@909: @opened = [] Chris@909: @subcolors = nil Chris@909: end Chris@909: Chris@909: public Chris@909: Chris@909: def text_token text, kind Chris@909: if color = (@subcolors || TOKEN_COLORS)[kind] Chris@909: if Hash === color Chris@909: if color[:self] Chris@909: color = color[:self] Chris@909: else Chris@909: @out << text Chris@909: return Chris@909: end Chris@909: end Chris@909: Chris@909: @out << ansi_colorize(color) Chris@909: @out << text.gsub("\n", ansi_clear + "\n" + ansi_colorize(color)) Chris@909: @out << ansi_clear Chris@909: @out << ansi_colorize(@subcolors[:self]) if @subcolors && @subcolors[:self] Chris@909: else Chris@909: @out << text Chris@909: end Chris@909: end Chris@909: Chris@909: def begin_group kind Chris@909: @opened << kind Chris@909: @out << open_token(kind) Chris@909: end Chris@909: alias begin_line begin_group Chris@909: Chris@909: def end_group kind Chris@909: if @opened.empty? Chris@909: # nothing to close Chris@909: else Chris@909: @opened.pop Chris@909: @out << ansi_clear Chris@909: @out << open_token(@opened.last) Chris@909: end Chris@909: end Chris@909: Chris@909: def end_line kind Chris@909: if @opened.empty? Chris@909: # nothing to close Chris@909: else Chris@909: @opened.pop Chris@909: # whole lines to be highlighted, Chris@909: # eg. added/modified/deleted lines in a diff Chris@909: @out << "\t" * 100 + ansi_clear Chris@909: @out << open_token(@opened.last) Chris@909: end Chris@909: end Chris@909: Chris@909: private Chris@909: Chris@909: def open_token kind Chris@909: if color = TOKEN_COLORS[kind] Chris@909: if Hash === color Chris@909: @subcolors = color Chris@909: ansi_colorize(color[:self]) if color[:self] Chris@909: else Chris@909: @subcolors = {} Chris@909: ansi_colorize(color) Chris@909: end Chris@909: else Chris@909: @subcolors = nil Chris@909: '' Chris@909: end Chris@909: end Chris@909: Chris@909: def ansi_colorize(color) Chris@909: Array(color).map { |c| "\e[#{c}m" }.join Chris@909: end Chris@909: def ansi_clear Chris@909: ansi_colorize(0) Chris@909: end Chris@909: end Chris@909: end Chris@909: end