editorconfig 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/editor_config.rb +153 -49
- data/lib/editor_config/version.rb +1 -1
- metadata +32 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64a6b74e60347b7f0f46844b74d685a4cd137dab
|
4
|
+
data.tar.gz: 5f46d8b91c90c9b6c5a6fe92cf46445f88cd20ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc966a7f53f0e581f1ebc4eaadcf8e391681d592ee323b42628cf17995a11e833aee2d53773411941c543eef931d4ab53c17a3e0f47be0435ab46178de00e2cf
|
7
|
+
data.tar.gz: 7f0db69b7e53300db3d2fc095ec7cb6fa6cb5eb9d8d4288b18d0cc2e3b2ba5d18ca817a4d4b596e8792a865d4944c0619aff42c6103e41d3f79272b75548bcca
|
data/lib/editor_config.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require "editor_config/version"
|
2
2
|
|
3
3
|
module EditorConfig
|
4
|
+
# Public: Default config basename.
|
4
5
|
CONFIG_FILENAME = ".editorconfig".freeze
|
5
6
|
|
7
|
+
# Public: Universal property names.
|
8
|
+
#
|
9
|
+
# https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
|
6
10
|
INDENT_STYLE = "indent_style".freeze
|
7
11
|
INDENT_SIZE = "indent_size".freeze
|
8
12
|
TAB_WIDTH = "tab_width".freeze
|
@@ -12,22 +16,38 @@ module EditorConfig
|
|
12
16
|
INSERT_FINAL_NEWLINE = "insert_final_newline".freeze
|
13
17
|
MAX_LINE_LENGTH = "max_line_length".freeze
|
14
18
|
|
19
|
+
# Public: Possible boolean values.
|
20
|
+
#
|
21
|
+
# https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
|
15
22
|
TRUE = "true".freeze
|
16
23
|
FALSE = "false".freeze
|
17
24
|
|
25
|
+
# Public: Possible indent style values.
|
26
|
+
#
|
27
|
+
# https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#indent_style
|
18
28
|
SPACE = "space".freeze
|
19
29
|
TAB = "tab".freeze
|
20
30
|
|
31
|
+
# Public: Possible EOL values.
|
32
|
+
#
|
33
|
+
# https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#end_of_line
|
21
34
|
CR = "cr".freeze
|
22
35
|
LF = "lf".freeze
|
23
36
|
CRLF = "crlf".freeze
|
24
37
|
|
38
|
+
# Public: Possible charset values.
|
39
|
+
#
|
40
|
+
# https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#charset
|
25
41
|
LATIN1 = "latin1".freeze
|
26
42
|
UTF_8 = "utf-8".freeze
|
27
43
|
UTF_8_BOM = "utf-8-bom".freeze
|
28
44
|
UTF_16BE = "utf-16be".freeze
|
29
45
|
UTF_16LE = "utf-16le".freeze
|
30
46
|
|
47
|
+
# Internal: Default filename to use if path is too long or has too many
|
48
|
+
# components.
|
49
|
+
DEFAULT_FILENAME = "filename".freeze
|
50
|
+
|
31
51
|
# Internal: Maximum number of bytes to read per line. Lines over this limit
|
32
52
|
# will be truncated.
|
33
53
|
MAX_LINE = 200
|
@@ -40,69 +60,60 @@ module EditorConfig
|
|
40
60
|
# will be truncated.
|
41
61
|
MAX_PROPERTY_NAME = 500
|
42
62
|
|
43
|
-
#
|
44
|
-
#
|
45
|
-
|
46
|
-
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
63
|
+
# Internal: Maximum byte length of filename path. Paths over this limit will
|
64
|
+
# default to global "*" properties.
|
65
|
+
MAX_FILENAME = 4096
|
66
|
+
|
67
|
+
# Internal: Maximum number of directories a filename can have. Paths this
|
68
|
+
# deep will default to global "*" properties.
|
69
|
+
MAX_FILENAME_COMPONENTS = 25
|
70
|
+
|
71
|
+
# Public: Parse the contents of an `.editorconfig`.
|
51
72
|
#
|
52
|
-
#
|
53
|
-
# "indent_style" - :tab, :space or nil.
|
54
|
-
# "indent_size" - :tab, an integer between 1-64, or nil.
|
55
|
-
# "tab_width" - an integer between 1-64, or nil.
|
56
|
-
# "end_of_line" - :lf, :cr, :crlf or nil.
|
57
|
-
# "charset" - "latin1", "utf-8", "utf-8-bom", "utf-16be",
|
58
|
-
# "utf-16le" or nil.
|
59
|
-
# "trim_trailing_whitespace" - true, false or nil.
|
60
|
-
# "insert_final_newline" - true, false or nil.
|
73
|
+
# io - An IO or String with the raw contents of an `.editorconfig` file.
|
61
74
|
#
|
62
|
-
#
|
63
|
-
#
|
75
|
+
# Returns a tuple of a parsed Hash of information and a boolean flag if the
|
76
|
+
# file was marked as "root". The hash contains string keys of each section
|
77
|
+
# of the config file.
|
64
78
|
#
|
65
79
|
# An example hash would look like this:
|
66
80
|
# {
|
67
|
-
# "root" => true,
|
68
81
|
# "*.rb" => {
|
69
|
-
# "indent_style" =>
|
70
|
-
# "indent_size" => 2,
|
82
|
+
# "indent_style" => "space",
|
83
|
+
# "indent_size" => "2",
|
71
84
|
# "charset" => "utf-8"
|
72
85
|
# }
|
73
86
|
# }
|
87
|
+
#
|
74
88
|
def self.parse(io, version: SPEC_VERSION)
|
75
|
-
|
76
|
-
# raise ArgumentError, "editorconfig syntax must be valid UTF-8"
|
77
|
-
# end
|
78
|
-
|
79
|
-
root = false
|
80
|
-
out_hash = {}
|
89
|
+
config, root = {}, false
|
81
90
|
last_section = nil
|
82
91
|
|
83
92
|
io.each_line do |line|
|
84
|
-
|
85
|
-
|
93
|
+
line = line.sub(/\s+(;|#).+$/, "").chomp
|
94
|
+
case line
|
95
|
+
when /\Aroot(\s+)?\=(\s+)?true\Z/i
|
86
96
|
root = true
|
87
|
-
when /\A\[(?<name>.+)\]\Z/
|
97
|
+
when /\A\s*\[(?<name>.+)\]\s*\Z/
|
88
98
|
# section marker
|
89
99
|
last_section = Regexp.last_match[:name][0, MAX_SECTION_NAME]
|
90
|
-
|
91
|
-
when /\A(?<name>[[:word:]]+)
|
100
|
+
config[last_section] ||= {}
|
101
|
+
when /\A\s*(?<name>[[:word:]]+)\s*(\=|:)\s*(?<value>.+)\s*\Z/
|
92
102
|
match = Regexp.last_match
|
93
|
-
name, value = match[:name][0, MAX_PROPERTY_NAME], match[:value]
|
94
|
-
|
95
|
-
if last_section
|
96
|
-
out_hash[last_section][name] = value
|
97
|
-
else
|
98
|
-
out_hash[name] = value
|
99
|
-
end
|
103
|
+
name, value = match[:name][0, MAX_PROPERTY_NAME].strip, match[:value].strip
|
104
|
+
config[last_section][name] = value if last_section
|
100
105
|
end
|
101
106
|
end
|
102
107
|
|
103
|
-
return
|
108
|
+
return config, root
|
104
109
|
end
|
105
110
|
|
111
|
+
# Public: Normalize known universal properties.
|
112
|
+
#
|
113
|
+
# config - Hash configuration
|
114
|
+
# version - String spec version
|
115
|
+
#
|
116
|
+
# Returns new preprocessed Hash.
|
106
117
|
def self.preprocess(config, version: SPEC_VERSION)
|
107
118
|
config = config.reduce({}) { |h, (k, v)| h[k.downcase] = v; h }
|
108
119
|
|
@@ -138,29 +149,108 @@ module EditorConfig
|
|
138
149
|
config
|
139
150
|
end
|
140
151
|
|
152
|
+
# Internal: Temporary replacement constants used within fnmatch.
|
153
|
+
FNMATCH_ESCAPED_LBRACE = "FNMATCH-ESCAPED-LBRACE".freeze
|
154
|
+
FNMATCH_ESCAPED_RBRACE = "FNMATCH-ESCAPED-RBRACE".freeze
|
155
|
+
|
156
|
+
# Public: Test shell pattern against a path.
|
157
|
+
#
|
158
|
+
# Modeled after editorconfig/fnmatch.py.
|
159
|
+
# https://github.com/editorconfig/editorconfig-core-py/blob/master/editorconfig/fnmatch.py
|
160
|
+
#
|
161
|
+
# pattern - String shell pattern
|
162
|
+
# path - String pathname
|
163
|
+
#
|
164
|
+
# Returns true if path matches pattern, otherwise false.
|
141
165
|
def self.fnmatch?(pattern, path)
|
142
166
|
flags = File::FNM_PATHNAME | File::FNM_EXTGLOB
|
143
|
-
|
144
|
-
|
167
|
+
pattern = pattern.dup
|
168
|
+
|
169
|
+
pattern.gsub!(/(\{\w*\})/) {
|
170
|
+
$1.gsub("{", FNMATCH_ESCAPED_LBRACE).gsub("}", FNMATCH_ESCAPED_RBRACE)
|
171
|
+
}
|
172
|
+
pattern.gsub!(/(\{[^}]+$)/) {
|
173
|
+
$1.gsub("{", FNMATCH_ESCAPED_LBRACE)
|
174
|
+
}
|
175
|
+
pattern.gsub!(/^([^\{]+\})/) {
|
176
|
+
$1.gsub("}", FNMATCH_ESCAPED_RBRACE)
|
177
|
+
}
|
178
|
+
|
179
|
+
pattern.gsub!(/(\{(.*)\})/) {
|
180
|
+
bracket = $1
|
181
|
+
inner = $2
|
182
|
+
|
183
|
+
if inner =~ /^(\d+)\.\.(\d+)$/
|
184
|
+
"{#{($1.to_i..$2.to_i).to_a.join(",")}}"
|
185
|
+
elsif inner.include?(",")
|
186
|
+
bracket
|
187
|
+
else
|
188
|
+
"#{FNMATCH_ESCAPED_LBRACE}#{inner}#{FNMATCH_ESCAPED_RBRACE}"
|
189
|
+
end
|
190
|
+
}
|
191
|
+
|
192
|
+
pattern.gsub!(FNMATCH_ESCAPED_LBRACE, "\\{")
|
193
|
+
pattern.gsub!(FNMATCH_ESCAPED_RBRACE, "\\}")
|
194
|
+
|
195
|
+
pattern.gsub!(/\[(.*\/.*)\]/) {
|
196
|
+
"\\[#{$1}\\]"
|
197
|
+
}
|
198
|
+
|
199
|
+
patterns = []
|
200
|
+
|
201
|
+
# Expand "**" to match over path separators
|
202
|
+
# TODO: Optimize the number of patterns we need
|
203
|
+
patterns << pattern.gsub(/\/\*\*/, "/**/*")
|
204
|
+
patterns << pattern.gsub(/\/\*\*/, "")
|
205
|
+
patterns << pattern.gsub(/\*\*/, "**/*")
|
206
|
+
patterns << pattern.gsub(/\*\*/, "/**/*")
|
207
|
+
|
208
|
+
patterns.any? { |p| File.fnmatch?(p, path, flags) }
|
145
209
|
end
|
146
210
|
|
211
|
+
# Public: Load EditorConfig with a custom loader implementation.
|
212
|
+
#
|
213
|
+
# path - String filename on file system
|
214
|
+
# config - Basename of config to search for (default: .editorconfig)
|
215
|
+
#
|
216
|
+
# loader block
|
217
|
+
# config_path - String "path/to/.editorconfig" to attempt to read from
|
218
|
+
#
|
219
|
+
# Returns Hash of String properties and values.
|
147
220
|
def self.load(path, config: CONFIG_FILENAME, version: SPEC_VERSION)
|
148
221
|
hash = {}
|
149
222
|
|
150
|
-
|
223
|
+
# Use default filename if path is too long
|
224
|
+
path = DEFAULT_FILENAME if path.length > MAX_FILENAME
|
225
|
+
|
226
|
+
components = traverse(path)
|
227
|
+
|
228
|
+
# Use default filename if path has too many directories
|
229
|
+
path = DEFAULT_FILENAME if components.length > MAX_FILENAME_COMPONENTS
|
230
|
+
|
231
|
+
components.each do |subpath|
|
151
232
|
config_path = subpath == "" ? config : "#{subpath}/#{config}"
|
152
233
|
config_data = yield config_path
|
153
234
|
next unless config_data
|
154
235
|
|
155
|
-
|
236
|
+
sections, root = parse(config_data, version: version)
|
237
|
+
section_properties = {}
|
156
238
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
239
|
+
sections.each do |section, properties|
|
240
|
+
if section.include?("/")
|
241
|
+
section = section[1..-1] if section[0] == "/"
|
242
|
+
pattern = subpath == "" ? section : "#{subpath}/#{section}"
|
243
|
+
else
|
244
|
+
pattern = "**/#{section}"
|
245
|
+
end
|
246
|
+
|
247
|
+
if fnmatch?(pattern, path)
|
248
|
+
section_properties.merge!(properties)
|
161
249
|
end
|
162
250
|
end
|
163
251
|
|
252
|
+
hash = section_properties.merge(hash)
|
253
|
+
|
164
254
|
if root
|
165
255
|
break
|
166
256
|
end
|
@@ -169,6 +259,11 @@ module EditorConfig
|
|
169
259
|
hash
|
170
260
|
end
|
171
261
|
|
262
|
+
# Internal: Generate subpaths for given path walking upwards to the root.
|
263
|
+
#
|
264
|
+
# path - String pathname
|
265
|
+
#
|
266
|
+
# Returns an Array of String paths.
|
172
267
|
def self.traverse(path)
|
173
268
|
paths = []
|
174
269
|
parts = path.split("/", -1)
|
@@ -189,6 +284,15 @@ module EditorConfig
|
|
189
284
|
paths
|
190
285
|
end
|
191
286
|
|
287
|
+
# Public: Load EditorConfig for a specific file.
|
288
|
+
#
|
289
|
+
# Starts at filename and walks up each directory gathering any .editorconfig
|
290
|
+
# files until it reaches a config marked as "root".
|
291
|
+
#
|
292
|
+
# path - String filename on file system
|
293
|
+
# config - Basename of config to search for (default: .editorconfig)
|
294
|
+
#
|
295
|
+
# Returns Hash of String properties and values.
|
192
296
|
def self.load_file(*args)
|
193
297
|
EditorConfig.load(*args) do |path|
|
194
298
|
File.read(path) if File.exist?(path)
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: editorconfig
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
12
|
-
dependencies:
|
11
|
+
date: 2015-05-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
13
41
|
description:
|
14
42
|
email:
|
15
43
|
executables: []
|
@@ -19,7 +47,7 @@ files:
|
|
19
47
|
- lib/editor_config.rb
|
20
48
|
- lib/editor_config/version.rb
|
21
49
|
- lib/editorconfig.rb
|
22
|
-
homepage:
|
50
|
+
homepage: https://github.com/editorconfig/editorconfig-core-ruby
|
23
51
|
licenses:
|
24
52
|
- MIT
|
25
53
|
metadata: {}
|