summaryrefslogtreecommitdiff
path: root/lib/coderay/helpers/file_type.rb
blob: a5d83ff2f8714ba6b2ab5101209fecb48b094885 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
module CodeRay
  
  # = FileType
  #
  # A simple filetype recognizer.
  #
  # == Usage
  #
  #  # determine the type of the given
  #  lang = FileType[file_name]
  #  
  #  # return :text if the file type is unknown
  #  lang = FileType.fetch file_name, :text
  #  
  #  # try the shebang line, too
  #  lang = FileType.fetch file_name, :text, true
  module FileType
    
    UnknownFileType = Class.new Exception
    
    class << self
      
      # Try to determine the file type of the file.
      #
      # +filename+ is a relative or absolute path to a file.
      #
      # The file itself is only accessed when +read_shebang+ is set to true.
      # That means you can get filetypes from files that don't exist.
      def [] filename, read_shebang = false
        name = File.basename filename
        ext = File.extname(name).sub(/^\./, '')  # from last dot, delete the leading dot
        ext2 = filename.to_s[/\.(.*)/, 1]  # from first dot
        
        type =
          TypeFromExt[ext] ||
          TypeFromExt[ext.downcase] ||
          (TypeFromExt[ext2] if ext2) ||
          (TypeFromExt[ext2.downcase] if ext2) ||
          TypeFromName[name] ||
          TypeFromName[name.downcase]
        type ||= shebang(filename) if read_shebang
        
        type
      end
      
      # This works like Hash#fetch.
      #
      # If the filetype cannot be found, the +default+ value
      # is returned.
      def fetch filename, default = nil, read_shebang = false
        if default && block_given?
          warn 'Block supersedes default value argument; use either.'
        end
        
        if type = self[filename, read_shebang]
          type
        else
          return yield if block_given?
          return default if default
          raise UnknownFileType, 'Could not determine type of %p.' % filename
        end
      end
      
    protected
      
      def shebang filename
        return unless File.exist? filename
        File.open filename, 'r' do |f|
          if first_line = f.gets
            if type = first_line[TypeFromShebang]
              type.to_sym
            end
          end
        end
      end
      
    end
    
    TypeFromExt = {
      'c'        => :c,
      'cfc'      => :xml,
      'cfm'      => :xml,
      'clj'      => :clojure,
      'css'      => :css,
      'diff'     => :diff,
      'dpr'      => :delphi,
      'erb'      => :erb,
      'gemspec'  => :ruby,
      'groovy'   => :groovy,
      'gvy'      => :groovy,
      'h'        => :c,
      'haml'     => :haml,
      'htm'      => :html,
      'html'     => :html,
      'html.erb' => :erb,
      'java'     => :java,
      'js'       => :java_script,
      'json'     => :json,
      'mab'      => :ruby,
      'pas'      => :delphi,
      'patch'    => :diff,
      'phtml'    => :php,
      'php'      => :php,
      'php3'     => :php,
      'php4'     => :php,
      'php5'     => :php,
      'prawn'    => :ruby,
      'py'       => :python,
      'py3'      => :python,
      'pyw'      => :python,
      'rake'     => :ruby,
      'raydebug' => :raydebug,
      'rb'       => :ruby,
      'rbw'      => :ruby,
      'rhtml'    => :erb,
      'rjs'      => :ruby,
      'rpdf'     => :ruby,
      'ru'       => :ruby,
      'rxml'     => :ruby,
      'sass'     => :sass,
      'sql'      => :sql,
      'tmproj'   => :xml,
      'xaml'     => :xml,
      'xhtml'    => :html,
      'xml'      => :xml,
      'yaml'     => :yaml,
      'yml'      => :yaml,
    }
    for cpp_alias in %w[cc cpp cp cxx c++ C hh hpp h++ cu]
      TypeFromExt[cpp_alias] = :cpp
    end
    
    TypeFromShebang = /\b(?:ruby|perl|python|sh)\b/
    
    TypeFromName = {
      'Capfile'  => :ruby,
      'Rakefile' => :ruby,
      'Rantfile' => :ruby,
      'Gemfile'  => :ruby,
    }
    
  end
  
end