Chris@909: module CodeRay Chris@909: Chris@909: # = FileType Chris@909: # Chris@909: # A simple filetype recognizer. Chris@909: # Chris@909: # == Usage Chris@909: # Chris@909: # # determine the type of the given Chris@909: # lang = FileType[file_name] Chris@909: # Chris@909: # # return :text if the file type is unknown Chris@909: # lang = FileType.fetch file_name, :text Chris@909: # Chris@909: # # try the shebang line, too Chris@909: # lang = FileType.fetch file_name, :text, true Chris@909: module FileType Chris@909: Chris@909: UnknownFileType = Class.new Exception Chris@909: Chris@909: class << self Chris@909: Chris@909: # Try to determine the file type of the file. Chris@909: # Chris@909: # +filename+ is a relative or absolute path to a file. Chris@909: # Chris@909: # The file itself is only accessed when +read_shebang+ is set to true. Chris@909: # That means you can get filetypes from files that don't exist. Chris@909: def [] filename, read_shebang = false Chris@909: name = File.basename filename Chris@909: ext = File.extname(name).sub(/^\./, '') # from last dot, delete the leading dot Chris@909: ext2 = filename.to_s[/\.(.*)/, 1] # from first dot Chris@909: Chris@909: type = Chris@909: TypeFromExt[ext] || Chris@909: TypeFromExt[ext.downcase] || Chris@909: (TypeFromExt[ext2] if ext2) || Chris@909: (TypeFromExt[ext2.downcase] if ext2) || Chris@909: TypeFromName[name] || Chris@909: TypeFromName[name.downcase] Chris@909: type ||= shebang(filename) if read_shebang Chris@909: Chris@909: type Chris@909: end Chris@909: Chris@909: # This works like Hash#fetch. Chris@909: # Chris@909: # If the filetype cannot be found, the +default+ value Chris@909: # is returned. Chris@909: def fetch filename, default = nil, read_shebang = false Chris@909: if default && block_given? Chris@909: warn 'Block supersedes default value argument; use either.' Chris@909: end Chris@909: Chris@909: if type = self[filename, read_shebang] Chris@909: type Chris@909: else Chris@909: return yield if block_given? Chris@909: return default if default Chris@909: raise UnknownFileType, 'Could not determine type of %p.' % filename Chris@909: end Chris@909: end Chris@909: Chris@909: protected Chris@909: Chris@909: def shebang filename Chris@909: return unless File.exist? filename Chris@909: File.open filename, 'r' do |f| Chris@909: if first_line = f.gets Chris@909: if type = first_line[TypeFromShebang] Chris@909: type.to_sym Chris@909: end Chris@909: end Chris@909: end Chris@909: end Chris@909: Chris@909: end Chris@909: Chris@909: TypeFromExt = { Chris@909: 'c' => :c, Chris@909: 'cfc' => :xml, Chris@909: 'cfm' => :xml, Chris@909: 'clj' => :clojure, Chris@909: 'css' => :css, Chris@909: 'diff' => :diff, Chris@909: 'dpr' => :delphi, Chris@909: 'gemspec' => :ruby, Chris@909: 'groovy' => :groovy, Chris@909: 'gvy' => :groovy, Chris@909: 'h' => :c, Chris@909: 'haml' => :haml, Chris@909: 'htm' => :page, Chris@909: 'html' => :page, Chris@909: 'html.erb' => :erb, Chris@909: 'java' => :java, Chris@909: 'js' => :java_script, Chris@909: 'json' => :json, Chris@909: 'mab' => :ruby, Chris@909: 'pas' => :delphi, Chris@909: 'patch' => :diff, Chris@909: 'php' => :php, Chris@909: 'php3' => :php, Chris@909: 'php4' => :php, Chris@909: 'php5' => :php, Chris@909: 'prawn' => :ruby, Chris@909: 'py' => :python, Chris@909: 'py3' => :python, Chris@909: 'pyw' => :python, Chris@909: 'rake' => :ruby, Chris@909: 'raydebug' => :raydebug, Chris@909: 'rb' => :ruby, Chris@909: 'rbw' => :ruby, Chris@909: 'rhtml' => :erb, Chris@909: 'rjs' => :ruby, Chris@909: 'rpdf' => :ruby, Chris@909: 'ru' => :ruby, Chris@909: 'rxml' => :ruby, Chris@909: # 'sch' => :scheme, Chris@909: 'sql' => :sql, Chris@909: # 'ss' => :scheme, Chris@909: 'xhtml' => :page, Chris@909: 'xml' => :xml, Chris@909: 'yaml' => :yaml, Chris@909: 'yml' => :yaml, Chris@909: } Chris@909: for cpp_alias in %w[cc cpp cp cxx c++ C hh hpp h++ cu] Chris@909: TypeFromExt[cpp_alias] = :cpp Chris@909: end Chris@909: Chris@909: TypeFromShebang = /\b(?:ruby|perl|python|sh)\b/ Chris@909: Chris@909: TypeFromName = { Chris@909: 'Capfile' => :ruby, Chris@909: 'Rakefile' => :ruby, Chris@909: 'Rantfile' => :ruby, Chris@909: 'Gemfile' => :ruby, Chris@909: } Chris@909: Chris@909: end Chris@909: Chris@909: end