Module: EditorConfig
- Defined in:
- lib/editor_config.rb,
lib/editor_config/version.rb
Constant Summary collapse
- CONFIG_FILENAME =
Public: Default config basename.
".editorconfig".freeze
- INDENT_STYLE =
Public: Universal property names.
github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
"indent_style".freeze
- INDENT_SIZE =
"indent_size".freeze
- TAB_WIDTH =
"tab_width".freeze
- END_OF_LINE =
"end_of_line".freeze
- CHARSET =
"charset".freeze
- TRIM_TRAILING_WHITESPACE =
"trim_trailing_whitespace".freeze
- INSERT_FINAL_NEWLINE =
"insert_final_newline".freeze
- MAX_LINE_LENGTH =
"max_line_length".freeze
- TRUE =
Public: Possible boolean values.
github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
"true".freeze
- FALSE =
"false".freeze
- SPACE =
Public: Possible indent style values.
github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#indent_style
"space".freeze
- TAB =
"tab".freeze
- CR =
Public: Possible EOL values.
github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#end_of_line
"cr".freeze
- LF =
"lf".freeze
- CRLF =
"crlf".freeze
- LATIN1 =
Public: Possible charset values.
github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#charset
"latin1".freeze
- UTF_8 =
"utf-8".freeze
- UTF_8_BOM =
"utf-8-bom".freeze
- UTF_16BE =
"utf-16be".freeze
- UTF_16LE =
"utf-16le".freeze
- DEFAULT_FILENAME =
Internal: Default filename to use if path is too long or has too many components.
"filename".freeze
- MAX_FILENAME =
Internal: Maximum byte length of filename path. Paths over this limit will default to global “*” properties.
4096
- MAX_FILENAME_COMPONENTS =
Internal: Maximum number of directories a filename can have. Paths this deep will default to global “*” properties.
25
- FNMATCH_ESCAPED_LBRACE =
Internal: Temporary replacement constants used within fnmatch.
"FNMATCH-ESCAPED-LBRACE".freeze
- FNMATCH_ESCAPED_RBRACE =
"FNMATCH-ESCAPED-RBRACE".freeze
- VERSION =
"0.2.2"
- SPEC_VERSION =
"0.9.1"
Class Method Summary collapse
-
.fnmatch?(pattern, path) ⇒ Boolean
Public: Test shell pattern against a path.
-
.load(path, config: CONFIG_FILENAME, version: SPEC_VERSION) ⇒ Object
Public: Load EditorConfig with a custom loader implementation.
-
.load_file(*args) ⇒ Object
Public: Load EditorConfig for a specific file.
-
.parse(io, version: SPEC_VERSION) ⇒ Object
Public: Parse the contents of an ‘.editorconfig`.
-
.preprocess(config, version: SPEC_VERSION) ⇒ Object
Public: Normalize known universal properties.
-
.traverse(path) ⇒ Object
Internal: Generate subpaths for given path walking upwards to the root.
Class Method Details
.fnmatch?(pattern, path) ⇒ Boolean
Public: Test shell pattern against a path.
Modeled after editorconfig/fnmatch.py.
https://github.com/editorconfig/editorconfig-core-py/blob/master/editorconfig/fnmatch.py
pattern - String shell pattern path - String pathname
Returns true if path matches pattern, otherwise false.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/editor_config.rb', line 153 def self.fnmatch?(pattern, path) flags = File::FNM_PATHNAME | File::FNM_EXTGLOB pattern = pattern.dup pattern.gsub!(/(\{\w*\})/) { $1.gsub("{", FNMATCH_ESCAPED_LBRACE).gsub("}", FNMATCH_ESCAPED_RBRACE) } pattern.gsub!(/(\{[^}]+$)/) { $1.gsub("{", FNMATCH_ESCAPED_LBRACE) } pattern.gsub!(/^([^\{]+\})/) { $1.gsub("}", FNMATCH_ESCAPED_RBRACE) } pattern.gsub!(/(\{(.*)\})/) { bracket = $1 inner = $2 if inner =~ /^(\d+)\.\.(\d+)$/ "{#{($1.to_i..$2.to_i).to_a.join(",")}}" elsif inner.include?(",") bracket else "#{FNMATCH_ESCAPED_LBRACE}#{inner}#{FNMATCH_ESCAPED_RBRACE}" end } pattern.gsub!(FNMATCH_ESCAPED_LBRACE, "\\{") pattern.gsub!(FNMATCH_ESCAPED_RBRACE, "\\}") pattern.gsub!(/\[(.*\/.*)\]/) { "\\[#{$1}\\]" } patterns = [] # Expand "**" to match over path separators # TODO: Optimize the number of patterns we need patterns << pattern.gsub(/\/\*\*/, "/**/*") patterns << pattern.gsub(/\/\*\*/, "") patterns << pattern.gsub(/\*\*/, "**/*") patterns << pattern.gsub(/\*\*/, "/**/*") patterns.any? { |p| File.fnmatch?(p, path, flags) } end |
.load(path, config: CONFIG_FILENAME, version: SPEC_VERSION) ⇒ Object
Public: Load EditorConfig with a custom loader implementation.
path - String filename on file system config - Basename of config to search for (default: .editorconfig)
loader block
config_path - String "path/to/.editorconfig" to attempt to read from
Returns Hash of String properties and values.
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/editor_config.rb', line 208 def self.load(path, config: CONFIG_FILENAME, version: SPEC_VERSION) hash = {} # Use default filename if path is too long path = DEFAULT_FILENAME if path.length > MAX_FILENAME components = traverse(path) # Use default filename if path has too many directories path = DEFAULT_FILENAME if components.length > MAX_FILENAME_COMPONENTS components.each do |subpath| config_path = subpath == "" ? config : "#{subpath}/#{config}" config_data = yield config_path next unless config_data sections, root = parse(config_data, version: version) section_properties = {} sections.each do |section, properties| if section.include?("/") section = section[1..-1] if section[0] == "/" pattern = subpath == "" ? section : "#{subpath}/#{section}" else pattern = "**/#{section}" end if fnmatch?(pattern, path) section_properties.merge!(properties) end end hash = section_properties.merge(hash) if root break end end hash end |
.load_file(*args) ⇒ Object
Public: Load EditorConfig for a specific file.
Starts at filename and walks up each directory gathering any .editorconfig files until it reaches a config marked as “root”.
path - String filename on file system config - Basename of config to search for (default: .editorconfig)
Returns Hash of String properties and values.
284 285 286 287 288 |
# File 'lib/editor_config.rb', line 284 def self.load_file(*args) EditorConfig.load(*args) do |path| File.read(path) if File.exist?(path) end end |
.parse(io, version: SPEC_VERSION) ⇒ Object
Public: Parse the contents of an ‘.editorconfig`.
io - An IO or String with the raw contents of an ‘.editorconfig` file.
Returns a tuple of a parsed Hash of information and a boolean flag if the file was marked as “root”. The hash contains string keys of each section of the config file.
An example hash would look like this: {
"*.rb" => {
"indent_style" => "space",
"indent_size" => "2",
"charset" => "utf-8"
}
}
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/editor_config.rb', line 76 def self.parse(io, version: SPEC_VERSION) config, root = {}, false last_section = nil io.each_line do |line| line = line.sub(/\s+(;|#).+$/, "").chomp case line when /\Aroot(\s+)?\=(\s+)?true\Z/i root = true when /\A\s*\[(?<name>.{1,4096})\]\s*\Z/ # section marker last_section = Regexp.last_match[:name] config[last_section] ||= {} when /\A\s*(?<name>[[:word:]]{1,50})\s*(\=|:)\s*(?<value>.{1,255})\s*\Z/ match = Regexp.last_match name, value = match[:name].strip, match[:value].strip config[last_section][name] = value if last_section end end return config, root end |
.preprocess(config, version: SPEC_VERSION) ⇒ Object
Public: Normalize known universal properties.
config - Hash configuration version - String spec version
Returns new preprocessed Hash.
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 |
# File 'lib/editor_config.rb', line 105 def self.preprocess(config, version: SPEC_VERSION) config = config.reduce({}) { |h, (k, v)| h[k.downcase] = v; h } [ INDENT_STYLE, INDENT_SIZE, TAB_WIDTH, END_OF_LINE, CHARSET, TRIM_TRAILING_WHITESPACE, INSERT_FINAL_NEWLINE, MAX_LINE_LENGTH ].each do |key| if config.key?(key) config[key] = config[key].downcase end end if !config.key?(TAB_WIDTH) && config.key?(INDENT_SIZE) && config[INDENT_SIZE] != TAB config[TAB_WIDTH] = config[INDENT_SIZE] end if version > "0.9" if !config.key?(INDENT_SIZE) && config[INDENT_STYLE] == TAB if config.key?(TAB_WIDTH) config[INDENT_SIZE] = config[TAB_WIDTH] else config[INDENT_SIZE] = TAB end end end config end |
.traverse(path) ⇒ Object
Internal: Generate subpaths for given path walking upwards to the root.
path - String pathname
Returns an Array of String paths.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/editor_config.rb', line 255 def self.traverse(path) paths = [] parts = path.split("/", -1) idx = parts.length - 1 while idx > 0 paths << parts[0, idx].join("/") idx -= 1 end if path.start_with?("/") paths[-1] = "/" else paths << "" end paths end |