AutoRunner

秀丸はマクロのrun文で他のアプリケーションを走らせると
新しい秀丸に出力をリダイレクトしてくれるので、
F5あたりで書いてるソースの種類で適当に処理系選んで走らせて
くれると嬉しい。

ので、そーいうのを書いてExerbで.exeに固めてパスを通してマクロを書いた。


autorunner.rb

アプリケーション。「autorunner FILE」で。

require 'configparser'
require 'optparse'

begin
  require 'win32/open3'
rescue LoadError
  require 'open3'
end

CONFIG_NAME = '.autorc'


def main
  opt = create_optionparser(
    File.basename($0),
    "Usage: %s [options] FILE",
    ['-h',
     '--help',
     'show this help',
     lambda{ puts opt.help ; exit }]
  )
  begin
    opt.parse!(ARGV)
    fname = parse_commandline(ARGV)
    path = search_file(CONFIG_NAME, ENV['AUTORC'], ENV['HOME'], '.') or
      raise LoadError, "`#{CONFIG_NAME}' not found"
  rescue OptionParser::ParseError, ArgumentError, LoadError => ex
    opt.error(ex.message)
  end
  begin
    table = AutoRunner::ConfigParser.load(path).export({})
    app = table[extension(fname)] or
      raise ArgumentError, "association not found for `#{fname}'"
  rescue AutoRunner::ConfigParser::ParseError, ArgumentError => ex
    opt.error(ex.message, '', <<-EOS, '')
  Associations
  ------------
    #{table.map{|ext, app| "#{ext}:#{app}" }.join('  ')}
    EOS
  end
  execute(app, fname)
end



# # # # # # # # # #

def execute(application, file)
  output = nil
  Open3.popen3("#{application} #{file}") do |inn, out, err|
    inn.close_write
    error = err.read
    $stderr.puts error unless error.empty?
    output = out.read
  end
  puts output
end

def parse_commandline(args)
  raise ArgumentError, "no argument" if args.empty?
  raise ArgumentError, "too many arguments (#{ARGV.size} for 1)" if args[1]
  args[0]
end

def search_file(base, *dirs)
  dirs.select{|dir| dir }.map{|dir|
    File.join(dir, base)
  }.find{|x| File.file?(x) }
end

def extension(path)
  fname = File.basename(path)
  i = fname.rindex('.') and fname[i+1..-1]
end

def create_optionparser(name, banner, *on)
  opt = OptionParser.new
  opt.program_name = name
  opt.banner = sprintf(banner, name)
  def opt.error(msg=nil, *others)
    $stderr.puts "#{program_name}: #{msg}" if msg
    $stderr.puts others
    $stderr.puts help
    exit 1
  end
  on.each do |short, long, desc, block|
    opt.on(short, long, desc, &block)
  end
  opt
end


# # # #
main

configparser.rb

ライブラリ。設定読み込み用。

module AutoRunner
  class ConfigParser
    class ParseError < RuntimeError ; end
    
    def self.load(path) ; new().load(path) ; end
    def self.for_io(f)  ; new().for_io(f)  ; end
    def self.parse(src) ; new().parse(src) ; end
    
    def load(path)
      path == '-' ? for_io($stdin) : parse(File.read(path))
    end
    
    def for_io(f)
      parse(f.read)
    end
    
    def parse(src)
      begin
        instance_eval(src)
      rescue NameError, NoMethodError => ex
        n = ex.backtrace.find{|b| /\A\(eval\)/ =~ b }.split(':')[1].to_s
        raise ParseError, "invalid format in line #{n}"
      end
      self
    end
    
    def initialize
      @__tbl__ = {}
    end
    
    def method_missing(name, *args)
      super if args.empty?
      name = name.to_s
      opts, args = args.partition{|x| x.kind_of?(Hash) }
      unless opts.empty?
        opts = opts.map{|h| h[:option] }.flatten
        name = "#{name} #{opts.join(' ')}"
      end
      args.each{|ext| @__tbl__[ext] = name }
    end
    
    def export(obj)
      @__tbl__.each{|app, exts| obj[app] = exts }
      obj
    end
  end
end

.autorc

自分用の設定ファイル。$AUTORCかホームディレクトリに置く。

gcc  "c"
ruby "rb"
racc "ry"
rd2  "rd"
perl "pl"
dmd  "d", :option => "-run"