Text::VimColor on Ruby and Windows(2)
あれか? 魔改造は俺の病気か何かか?
しかもはてダラといっしょで根っこは人様のコードに頼りっきりっ
てんだからまあ。
vimcolor.rb(魔改)
こんな感じに。
ライセンスは元々と同じでGPLです。
改造のメモとか。
・メソッド分割 ・Vimの位置 ・run_fileのインタフェースのちょい変更 ・gsubの連続による置換をgsub+テーブルに置き換え ・元々Win32用forkを使うつもりだったが、なんかプロセスの無間地獄になってうわあああぁぁぁあ ・なのでWin32用Open3を使うことに ・XMLエスケープの実装をモジュールに ・Tempfileのリソース管理をブロックで
# # A ported version of Perl's Text::VimColor to Ruby. # # modified by arikui(shionist@yahoo.co.jp) # require 'strscan' require 'tempfile' begin require 'win32/open3' rescue LoadError require 'open3' end class VimColor VIM_COMMAND = File.join(ENV['VIM'], 'vim') VIM_OPTIONS = %w[-R -X -Z -i NONE -u NONE -N] VIM_PRESET = ["+set nomodeline", '+set expandtab'] # +set shiftwidth VIM_POSTSET = [":let b:is_bash=1", ":filetype on"] VIM_MARK_SCRIPT = File.join(File.dirname(__FILE__), 'vimcolor', 'mark.vim') private def vim_input(path) <<-EOS #{@vim_postset.join("\n")} :source #{VIM_MARK_SCRIPT} :write! #{path} :qall! EOS end def initialize(command = VIM_COMMAND, options = VIM_OPTIONS, preset = VIM_PRESET , postset = VIM_POSTSET) @vim_command = command @vim_options = options @vim_preset = preset @vim_postset = postset end def vim_execute(path) result = nil Tempfile.open('ruby-vimcolor') do |t| t.puts vim_input(t.path) t.flush args = [@vim_options, '-s', t.path, path, @vim_preset].flatten Open3.popen3("#{@vim_command} #{args.join(' ')}") do |inn, out, err| inn.close_write ; out.read ; err.read end t.rewind result = t.read end result end ARRANGE = { '&l' => '<', '&g' => '>', '&a' => '&' } def format_arrange(vimout, formatter) s = StringScanner.new(vimout) tbl = ARRANGE #optimize while until_matched = s.scan_until(/>.*?>/) matched = s.matched text = until_matched[0..(-matched.length - 1)] type = matched[1..(matched.length - 2)] if s.scan_until(/.*?<#{Regexp.escape(type)}</s) text << s.matched[0..-matched.length-1] text = text.gsub(Regexp.union(*tbl.keys)){|m| tbl[m] } formatter.push(type, text) end end formatter.result end public #options <- Hash-like one (Hash, Struct, ...) def run_file(path, options, formatter_class, *formatter_args) formatter_class.respond_to?(:new) or formatter_class = self.class.const_get("Format_#{formatter_class}") @vim_postset << ":set filetype=#{options[:filetype]}" if options[:filetype] @vim_preset << "+set encoding=#{options[:encoding]}" if options[:encoding] vimout = vim_execute(path) format_arrange(vimout, formatter_class.new(*formatter_args)) end def run(str, file_type, formatter_class, *formatter_args) Tempfile.open('ruby-vimcolor-input') do |t| t.write(str) t.flush run_file(t.path, file_type, formatter_class, *formatter_args) end end def run_stream(stream, file_type, formatter_class, *formatter_args) run(stream.read, file_type, formatter_class, *formatter_args) end class Format_array def initialize @result = [] end def push(type, text) @result.push [type, text] end attr_reader :result end module EscapeXML ESC = { "&" => "&", "<" => "<", ">" => ">", "'" => "'", '"' => """ } def escape_xml(text) tbl = ESC #optimize text.gsub(Regexp.union(*tbl.keys)){|m| tbl[m] } end end class Format_xml include EscapeXML def initialize @result = '' end def push(type, text) type = 'Normal' if type.empty? @result << %[<#{type}>#{escape_xml(text)}</#{type}>] end attr_reader :result end class Format_html include EscapeXML def initialize(class_prefix = 'syn') @result = '' @prefix = class_prefix end def push(type, text) text = escape_xml(text) @result.concat( type.empty? ? text : %[<span class="#{@prefix}#{type}">#{text}</span>] ) end attr_reader :result end class Format_ansi AnsiCodes = { :normal => 0, :reset => 0, :bold => 1, :dark => 2, :italic => 3, :underline => 4, :blink => 5, :rapid_blink => 6, :negative => 7, :concealed => 8, :strikethrough => 9, :black => 30, :red => 31, :green => 32, :yellow => 33, :blue => 34, :magenta => 35, :cyan => 36, :white => 37, :on_black => 40, :on_red => 41, :on_green => 42, :on_yellow => 43, :on_blue => 44, :on_magenta => 45, :on_cyan => 46, :on_white => 47, } def initialize(colors = {}) @result = '' @colors = Hash.new([]) @colors.merge!({ 'Comment' => [ :blue ], 'Constant' => [ :red ], 'Identifier' => [ :green ], 'Statement' => [ :yellow ], 'PreProc' => [ :magenta ], 'Type' => [ :green ], 'Special' => [ :magenta ], 'Underlined' => [ :underline ], 'Error' => [ :red ], 'Todo' => [ :black, :on_yellow ], }) @colors.merge!(colors) end def push(type, text) seq = '' codes = @colors[type] codes.unshift(:reset) codes.each {|c| num = AnsiCodes[c] seq << "\e[#{num}m" if num } @result << seq << text end def result @result << "\e[0m" @result end end end
vc.rb
こっちはvimcolor-embedみたいなコマンド。
本家のはちょっとノリが合わなかったので。
Usage: vc [options] [FILE] -t, --filetype=SYNTAX assist Vim to guess syntax type -f, --format=FORMAT set output format(ansi|xml|html=default) -o, --output=PATH output to a file which specfied -e, --encoding=CODE set encoding(default=UTF-8) -h, --help show this help
#!/usr/bin/env ruby require 'vimcolor' require 'optparse' def main options = create_option() opt = create_optionparser( File.basename($0, '.rb'), "Usage: %s [options] [FILE]", ['-t', '--filetype=SYNTAX', 'assist Vim to guess syntax type', lambda{|x| options.filetype = x }], ['-f', '--format=FORMAT', 'set output format(ansi|xml|html=default)', lambda{|x| options.format = x }], ['-o', '--output=PATH', 'output to a file which specfied', lambda{|x| options.output = x }], ['-e', '--encoding=CODE', 'set encoding(default=UTF-8)', lambda{|x| options.encoding = x }], ['-h', '--help', 'show this help', lambda{ puts opt.help ; exit }] ) begin opt.parse!(ARGV) file = nil if (file = ARGV.shift) == '-' ARGV.empty? or raise(ArgumentError, "too many arguments (#{ARGV.size + 1} for 1)") rescue OptionParser::ParseError, ArgumentError => ex opt.error(ex.message) end write_file(options.output) do |f| f.puts VimColor.new.send("run_#{file ? 'file' : 'stream'}", file || $stdin, #{ :filetype => options.filetype, :encoding => options.encoding }, options, options.format ) end end # # # # # # # # # # # # # # # # # def write_file(path, &block) path == '-' ? yield($stdout) : File.open(path, 'w', &block) end def create_option default = { :filetype => nil, :format => 'html',#:ansi, :output => '-', :encoding => 'utf-8', } options = Struct.new(*default.keys).new default.each{|k, v| options[k] = v } options end def create_optionparser(name, banner, *on) opt = OptionParser.new opt.program_name = name opt.banner = sprintf(banner, name) def opt.error(msg=nil) $stderr.puts "#{program_name}: #{msg}" if msg $stderr.puts help exit 1 end on.each do |short, long, desc, block| opt.on(short, long, desc, &block) end opt end # # # main