editorconfig 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: {}
|