File: test.swift.fold

package info (click to toggle)
kf6-syntax-highlighting 6.13.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 47,568 kB
  • sloc: xml: 197,750; cpp: 12,850; python: 3,023; sh: 955; perl: 546; ruby: 488; pascal: 393; javascript: 161; php: 150; jsp: 132; lisp: 131; haskell: 124; ada: 119; ansic: 107; makefile: 96; f90: 94; ml: 85; cobol: 81; yacc: 71; csh: 62; erlang: 54; sql: 51; java: 47; objc: 37; awk: 31; asm: 30; tcl: 29; fortran: 18; cs: 10
file content (696 lines) | stat: -rw-r--r-- 37,310 bytes parent folder | download
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
145
146
147
148
149
150
151
152
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
198
199
200
201
202
203
204
205
206
207
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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
//
//  Arguments.swift
//  SwiftFormat
//
//  Created by Nick Lockwood on 07/08/2018.
//  Copyright © 2018 Nick Lockwood.
//
//  Distributed under the permissive MIT license
//  Get the latest version from here:
//
//  https://github.com/nicklockwood/SwiftFormat
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//  SOFTWARE.
//

import Foundation

extension Options <beginfold id='1'>{</beginfold id='1'>
    static let maxArgumentNameLength = 16

    init(_ args: [String: String], in directory: String) throws <beginfold id='1'>{</beginfold id='1'>
        fileOptions = try fileOptionsFor(args, in: directory)
        formatOptions = try formatOptionsFor(args)
        let lint = args.keys.contains("lint")
        self.lint = lint
        rules = try rulesFor(args, lint: lint)
    <endfold id='1'>}</endfold id='1'>

    mutating func addArguments(_ args: [String: String], in directory: String) throws <beginfold id='1'>{</beginfold id='1'>
        let oldArguments = argumentsFor(self)
        let newArguments = try mergeArguments(args, into: oldArguments)
        var newOptions = try Options(newArguments, in: directory)
        if let fileInfo = formatOptions?.fileInfo <beginfold id='1'>{</beginfold id='1'>
            newOptions.formatOptions?.fileInfo = fileInfo
        <endfold id='1'>}</endfold id='1'>
        self = newOptions
    <endfold id='1'>}</endfold id='1'>
<endfold id='1'>}</endfold id='1'>

// Parse a space-delimited string into an array of command-line arguments
// Replicates the behavior implemented by the console when parsing input
func parseArguments(_ argumentString: String, ignoreComments: Bool = true) -> [String] <beginfold id='1'>{</beginfold id='1'>
    var arguments = [""] // Arguments always begin with script path
    var characters = String.UnicodeScalarView.SubSequence(argumentString.unicodeScalars)
    var string = ""
    var escaped = false
    var quoted = false
    loop: while let char = characters.popFirst() <beginfold id='1'>{</beginfold id='1'>
        switch char <beginfold id='1'>{</beginfold id='1'>
        case "#" where !ignoreComments && !escaped && !quoted:
            break loop // comment
        case "\\" where !escaped:
            escaped = true
        case "\"" where !escaped && !quoted:
            quoted = true
        case "\"" where !escaped && quoted:
            quoted = false
            fallthrough
        case " " where !escaped && !quoted:
            if !string.isEmpty <beginfold id='1'>{</beginfold id='1'>
                arguments.append(string)
            <endfold id='1'>}</endfold id='1'>
            string.removeAll()
        case "\"" where escaped:
            escaped = false
            string.append("\"")
        case _ where escaped && quoted:
            string.append("\\")
            fallthrough
        default:
            escaped = false
            string.append(Character(char))
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    if !string.isEmpty <beginfold id='1'>{</beginfold id='1'>
        arguments.append(string)
    <endfold id='1'>}</endfold id='1'>
    return arguments
<endfold id='1'>}</endfold id='1'>

// Parse a flat array of command-line arguments into a dictionary of flags and values
func preprocessArguments(_ args: [String], _ names: [String]) throws -> [String: String] <beginfold id='1'>{</beginfold id='1'>
    var anonymousArgs = 0
    var namedArgs: [String: String] = [:]
    var name = ""
    for arg in args <beginfold id='1'>{</beginfold id='1'>
        if arg.hasPrefix("--") <beginfold id='1'>{</beginfold id='1'>
            // Long argument names
            let key = String(arg.unicodeScalars.dropFirst(2))
            guard names.contains(key) else <beginfold id='1'>{</beginfold id='1'>
                guard let match = bestMatches(for: key, in: names).first else <beginfold id='1'>{</beginfold id='1'>
                    throw FormatError.options("Unknown option --\(key)")
                <endfold id='1'>}</endfold id='1'>
                throw FormatError.options("Unknown option --\(key). Did you mean --\(match)?")
            <endfold id='1'>}</endfold id='1'>
            name = key
            namedArgs[name] = namedArgs[name] ?? ""
            continue
        <endfold id='1'>}</endfold id='1'> else if arg.hasPrefix("-") <beginfold id='1'>{</beginfold id='1'>
            // Short argument names
            let flag = String(arg.unicodeScalars.dropFirst())
            guard let match = names.first(where: <beginfold id='1'>{</beginfold id='1'> $0.hasPrefix(flag) <endfold id='1'>}</endfold id='1'>) else <beginfold id='1'>{</beginfold id='1'>
                throw FormatError.options("Unknown flag -\(flag)")
            <endfold id='1'>}</endfold id='1'>
            name = match
            namedArgs[name] = namedArgs[name] ?? ""
            continue
        <endfold id='1'>}</endfold id='1'>
        if name == "" <beginfold id='1'>{</beginfold id='1'>
            // Argument is anonymous
            name = String(anonymousArgs)
            anonymousArgs += 1
        <endfold id='1'>}</endfold id='1'>
        var arg = arg
        let hasTrailingComma = arg.hasSuffix(",") && arg != ","
        if hasTrailingComma <beginfold id='1'>{</beginfold id='1'>
            arg = String(arg.dropLast())
        <endfold id='1'>}</endfold id='1'>
        if let existing = namedArgs[name], !existing.isEmpty,
           // TODO: find a more general way to represent merge-able options
           ["exclude", "unexclude", "disable", "enable", "lintonly", "rules"].contains(name) ||
           Descriptors.all.contains(where: <beginfold id='1'>{</beginfold id='1'>
               $0.argumentName == name && $0.isSetType
           <endfold id='1'>}</endfold id='1'>)
        <beginfold id='1'>{</beginfold id='1'>
            namedArgs[name] = existing + "," + arg
        <endfold id='1'>}</endfold id='1'> else <beginfold id='1'>{</beginfold id='1'>
            namedArgs[name] = arg
        <endfold id='1'>}</endfold id='1'>
        if !hasTrailingComma <beginfold id='1'>{</beginfold id='1'>
            name = ""
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    return namedArgs
<endfold id='1'>}</endfold id='1'>

// Find best match for a given string in a list of options
func bestMatches(for query: String, in options: [String]) -> [String] <beginfold id='1'>{</beginfold id='1'>
    let lowercaseQuery = query.lowercased()
    // Sort matches by Levenshtein edit distance
    return options
        .compactMap <beginfold id='1'>{</beginfold id='1'> option -> (String, distance: Int, commonPrefix: Int)? in
            let lowercaseOption = option.lowercased()
            let distance = editDistance(lowercaseOption, lowercaseQuery)
            let commonPrefix = lowercaseOption.commonPrefix(with: lowercaseQuery)
            if commonPrefix.isEmpty, distance > lowercaseQuery.count / 2 <beginfold id='1'>{</beginfold id='1'>
                return nil
            <endfold id='1'>}</endfold id='1'>
            return (option, distance, commonPrefix.count)
        <endfold id='1'>}</endfold id='1'>
        .sorted <beginfold id='1'>{</beginfold id='1'>
            if $0.distance == $1.distance <beginfold id='1'>{</beginfold id='1'>
                return $0.commonPrefix > $1.commonPrefix
            <endfold id='1'>}</endfold id='1'>
            return $0.distance < $1.distance
        <endfold id='1'>}</endfold id='1'>
        .map <beginfold id='1'>{</beginfold id='1'> $0.0 <endfold id='1'>}</endfold id='1'>
<endfold id='1'>}</endfold id='1'>

/// The Damerau-Levenshtein edit-distance between two strings
func editDistance(_ lhs: String, _ rhs: String) -> Int <beginfold id='1'>{</beginfold id='1'>
    let lhs = Array(lhs)
    let rhs = Array(rhs)
    var dist = [[Int]]()
    for i in 0 ... lhs.count <beginfold id='1'>{</beginfold id='1'>
        dist.append([i])
    <endfold id='1'>}</endfold id='1'>
    for j in 1 ... rhs.count <beginfold id='1'>{</beginfold id='1'>
        dist[0].append(j)
    <endfold id='1'>}</endfold id='1'>
    for i in 1 ... lhs.count <beginfold id='1'>{</beginfold id='1'>
        for j in 1 ... rhs.count <beginfold id='1'>{</beginfold id='1'>
            if lhs[i - 1] == rhs[j - 1] <beginfold id='1'>{</beginfold id='1'>
                dist[i].append(dist[i - 1][j - 1])
            <endfold id='1'>}</endfold id='1'> else <beginfold id='1'>{</beginfold id='1'>
                dist[i].append(min(dist[i - 1][j] + 1,
                                   dist[i][j - 1] + 1,
                                   dist[i - 1][j - 1] + 1))
            <endfold id='1'>}</endfold id='1'>
            if i > 1, j > 1, lhs[i - 1] == rhs[j - 2], lhs[i - 2] == rhs[j - 1] <beginfold id='1'>{</beginfold id='1'>
                dist[i][j] = min(dist[i][j], dist[i - 2][j - 2] + 1)
            <endfold id='1'>}</endfold id='1'>
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    return dist[lhs.count][rhs.count]
<endfold id='1'>}</endfold id='1'>

// Parse a comma-delimited list of items
func parseCommaDelimitedList(_ string: String) -> [String] <beginfold id='1'>{</beginfold id='1'>
    return string.components(separatedBy: ",").compactMap <beginfold id='1'>{</beginfold id='1'>
        let item = $0.trimmingCharacters(in: .whitespacesAndNewlines)
        return item.isEmpty ? nil : item
    <endfold id='1'>}</endfold id='1'>
<endfold id='1'>}</endfold id='1'>

// Parse a comma-delimited string into an array of rules
let allRules = Set(FormatRules.byName.keys)
func parseRules(_ rules: String) throws -> [String] <beginfold id='1'>{</beginfold id='1'>
    return try parseCommaDelimitedList(rules).map <beginfold id='1'>{</beginfold id='1'> proposedName in
        if let name = allRules.first(where: <beginfold id='1'>{</beginfold id='1'>
            $0.lowercased() == proposedName.lowercased()
        <endfold id='1'>}</endfold id='1'>) <beginfold id='1'>{</beginfold id='1'>
            return name
        <endfold id='1'>}</endfold id='1'>
        if Descriptors.all.contains(where: <beginfold id='1'>{</beginfold id='1'>
            $0.argumentName == proposedName
        <endfold id='1'>}</endfold id='1'>) <beginfold id='1'>{</beginfold id='1'>
            for rule in FormatRules.all where rule.options.contains(proposedName) <beginfold id='1'>{</beginfold id='1'>
                throw FormatError.options(
                    "'\(proposedName)' is not a formatting rule. Did you mean '\(rule.name)'?"
                )
            <endfold id='1'>}</endfold id='1'>
            throw FormatError.options("'\(proposedName)' is not a formatting rule")
        <endfold id='1'>}</endfold id='1'>
        guard let match = bestMatches(for: proposedName, in: Array(allRules)).first else <beginfold id='1'>{</beginfold id='1'>
            throw FormatError.options("Unknown rule '\(proposedName)'")
        <endfold id='1'>}</endfold id='1'>
        throw FormatError.options("Unknown rule '\(proposedName)'. Did you mean '\(match)'?")
    <endfold id='1'>}</endfold id='1'>
<endfold id='1'>}</endfold id='1'>

// Parse single file path, disallowing globs or commas
func parsePath(_ path: String, for argument: String, in directory: String) throws -> URL <beginfold id='1'>{</beginfold id='1'>
    let expandedPath = expandPath(path, in: directory)
    if !FileManager.default.fileExists(atPath: expandedPath.path) <beginfold id='1'>{</beginfold id='1'>
        if path.contains(",") <beginfold id='1'>{</beginfold id='1'>
            throw FormatError.options("\(argument) argument does not support multiple paths")
        <endfold id='1'>}</endfold id='1'>
        if pathContainsGlobSyntax(path) <beginfold id='1'>{</beginfold id='1'>
            throw FormatError.options("\(argument) path cannot contain wildcards")
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    return expandedPath
<endfold id='1'>}</endfold id='1'>

// Parse one or more comma-delimited file paths, expanding globs as required
func parsePaths(_ paths: String, in directory: String) throws -> [URL] <beginfold id='1'>{</beginfold id='1'>
    return try matchGlobs(expandGlobs(paths, in: directory), in: directory)
<endfold id='1'>}</endfold id='1'>

// Merge two dictionaries of arguments
func mergeArguments(_ args: [String: String], into config: [String: String]) throws -> [String: String] <beginfold id='1'>{</beginfold id='1'>
    var input = config
    var output = args
    // Merge excluded urls
    if let exclude = output["exclude"].map(parseCommaDelimitedList),
       var excluded = input["exclude"].map(<beginfold id='1'>{</beginfold id='1'> Set(parseCommaDelimitedList($0)) <endfold id='1'>}</endfold id='1'>)
    <beginfold id='1'>{</beginfold id='1'>
        excluded.formUnion(exclude)
        output["exclude"] = Array(excluded).sorted().joined(separator: ",")
    <endfold id='1'>}</endfold id='1'>
    // Merge unexcluded urls
    if let unexclude = output["unexclude"].map(parseCommaDelimitedList),
       var unexcluded = input["unexclude"].map(<beginfold id='1'>{</beginfold id='1'> Set(parseCommaDelimitedList($0)) <endfold id='1'>}</endfold id='1'>)
    <beginfold id='1'>{</beginfold id='1'>
        unexcluded.formUnion(unexclude)
        output["unexclude"] = Array(unexcluded).sorted().joined(separator: ",")
    <endfold id='1'>}</endfold id='1'>
    // Merge rules
    if let rules = try output["rules"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
        if rules.isEmpty <beginfold id='1'>{</beginfold id='1'>
            output["rules"] = nil
        <endfold id='1'>}</endfold id='1'> else <beginfold id='1'>{</beginfold id='1'>
            input["rules"] = nil
            input["enable"] = nil
            input["disable"] = nil
            input["lintonly"] = nil
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'> else <beginfold id='1'>{</beginfold id='1'>
        if let _disable = try output["disable"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
            if let rules = try input["rules"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
                input["rules"] = Set(rules).subtracting(_disable).sorted().joined(separator: ",")
            <endfold id='1'>}</endfold id='1'>
            if let enable = try input["enable"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
                input["enable"] = Set(enable).subtracting(_disable).sorted().joined(separator: ",")
            <endfold id='1'>}</endfold id='1'>
            if let lintonly = try input["lintonly"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
                input["lintonly"] = Set(lintonly).subtracting(_disable).sorted().joined(separator: ",")
            <endfold id='1'>}</endfold id='1'>
            if let disable = try input["disable"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
                input["disable"] = Set(disable).union(_disable).sorted().joined(separator: ",")
                output["disable"] = nil
            <endfold id='1'>}</endfold id='1'>
        <endfold id='1'>}</endfold id='1'>
        if let _enable = try output["enable"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
            if let enable = try input["enable"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
                input["enable"] = Set(enable).union(_enable).sorted().joined(separator: ",")
                output["enable"] = nil
            <endfold id='1'>}</endfold id='1'>
            if let lintonly = try input["lintonly"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
                input["lintonly"] = Set(lintonly).subtracting(_enable).sorted().joined(separator: ",")
            <endfold id='1'>}</endfold id='1'>
            if let disable = try input["disable"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
                input["disable"] = Set(disable).subtracting(_enable).sorted().joined(separator: ",")
            <endfold id='1'>}</endfold id='1'>
        <endfold id='1'>}</endfold id='1'>
        if let _lintonly = try output["lintonly"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
            if let lintonly = try input["lintonly"].map(parseRules) <beginfold id='1'>{</beginfold id='1'>
                input["lintonly"] = Set(lintonly).union(_lintonly).sorted().joined(separator: ",")
                output["lintonly"] = nil
            <endfold id='1'>}</endfold id='1'>
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    // Merge other arguments
    for (key, inValue) in input <beginfold id='1'>{</beginfold id='1'>
        guard let outValue = output[key] else <beginfold id='1'>{</beginfold id='1'>
            output[key] = inValue
            continue
        <endfold id='1'>}</endfold id='1'>
        if Descriptors.all.contains(where: <beginfold id='1'>{</beginfold id='1'> $0.argumentName == key && $0.isSetType <endfold id='1'>}</endfold id='1'>) <beginfold id='1'>{</beginfold id='1'>
            let inOptions = parseCommaDelimitedList(inValue)
            let outOptions = parseCommaDelimitedList(outValue)
            output[key] = Set(inOptions).union(outOptions).sorted().joined(separator: ",")
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    return output
<endfold id='1'>}</endfold id='1'>

// Parse a configuration file into a dictionary of arguments
public func parseConfigFile(_ data: Data) throws -> [String: String] <beginfold id='1'>{</beginfold id='1'>
    guard let input = String(data: data, encoding: .utf8) else <beginfold id='1'>{</beginfold id='1'>
        throw FormatError.reading("Unable to read data for configuration file")
    <endfold id='1'>}</endfold id='1'>
    let lines = try cumulate(successiveLines: input.components(separatedBy: .newlines))
    let arguments = try lines.flatMap <beginfold id='1'>{</beginfold id='1'> line -> [String] in
        // TODO: parseArguments isn't a perfect fit here - should we use a different approach?
        let line = line.replacingOccurrences(of: "\\n", with: "\n")
        let parts = parseArguments(line, ignoreComments: false).dropFirst().map <beginfold id='1'>{</beginfold id='1'>
            $0.replacingOccurrences(of: "\n", with: "\\n")
        <endfold id='1'>}</endfold id='1'>
        guard let key = parts.first else <beginfold id='1'>{</beginfold id='1'>
            return []
        <endfold id='1'>}</endfold id='1'>
        if !key.hasPrefix("-") <beginfold id='1'>{</beginfold id='1'>
            throw FormatError.options("Unknown option '\(key)' in configuration file")
        <endfold id='1'>}</endfold id='1'>
        return [key, parts.dropFirst().joined(separator: " ")]
    <endfold id='1'>}</endfold id='1'>
    do <beginfold id='1'>{</beginfold id='1'>
        return try preprocessArguments(arguments, optionsArguments)
    <endfold id='1'>}</endfold id='1'> catch let FormatError.options(message) <beginfold id='1'>{</beginfold id='1'>
        throw FormatError.options("\(message) in configuration file")
    <endfold id='1'>}</endfold id='1'>
<endfold id='1'>}</endfold id='1'>

private func cumulate(successiveLines: [String]) throws -> [String] <beginfold id='1'>{</beginfold id='1'>
    var cumulatedLines = [String]()
    var iterator = successiveLines.makeIterator()
    while let currentLine = iterator.next() <beginfold id='1'>{</beginfold id='1'>
        var cumulatedLine = effectiveContent(of: currentLine)
        while cumulatedLine.hasSuffix("\\") <beginfold id='1'>{</beginfold id='1'>
            guard let nextLine = iterator.next() else <beginfold id='1'>{</beginfold id='1'>
                throw FormatError.reading("Configuration file ends with an illegal line continuation character '\'")
            <endfold id='1'>}</endfold id='1'>
            if !nextLine.trimmingCharacters(in: .whitespaces).starts(with: "#") <beginfold id='1'>{</beginfold id='1'>
                cumulatedLine = cumulatedLine.dropLast() + effectiveContent(of: nextLine)
            <endfold id='1'>}</endfold id='1'>
        <endfold id='1'>}</endfold id='1'>
        cumulatedLines.append(String(cumulatedLine))
    <endfold id='1'>}</endfold id='1'>
    return cumulatedLines
<endfold id='1'>}</endfold id='1'>

private func effectiveContent(of line: String) -> String <beginfold id='1'>{</beginfold id='1'>
    return line
        .prefix <beginfold id='1'>{</beginfold id='1'> $0 != "#" <endfold id='1'>}</endfold id='1'>
        .trimmingCharacters(in: .whitespaces)
<endfold id='1'>}</endfold id='1'>

// Serialize a set of options into either an arguments string or a file
func serialize(options: Options,
               swiftVersion: Version = .undefined,
               excludingDefaults: Bool = false,
               separator: String = "\n") -> String
<beginfold id='1'>{</beginfold id='1'>
    var arguments = [[String: String]]()
    if let fileOptions = options.fileOptions <beginfold id='1'>{</beginfold id='1'>
        arguments.append(argumentsFor(
            Options(fileOptions: fileOptions),
            excludingDefaults: excludingDefaults
        ))
    <endfold id='1'>}</endfold id='1'>
    if let formatOptions = options.formatOptions <beginfold id='1'>{</beginfold id='1'>
        arguments.append(argumentsFor(
            Options(formatOptions: formatOptions),
            excludingDefaults: excludingDefaults
        ))
    <endfold id='1'>}</endfold id='1'> else if swiftVersion != .undefined <beginfold id='1'>{</beginfold id='1'>
        let descriptor = Descriptors.swiftVersion
        arguments.append([descriptor.argumentName: swiftVersion.rawValue])
    <endfold id='1'>}</endfold id='1'>
    if let rules = options.rules <beginfold id='1'>{</beginfold id='1'>
        arguments.append(argumentsFor(
            Options(rules: rules),
            excludingDefaults: excludingDefaults
        ))
    <endfold id='1'>}</endfold id='1'>
    return arguments
        .map <beginfold id='1'>{</beginfold id='1'> serialize(arguments: $0, separator: separator) <endfold id='1'>}</endfold id='1'>
        .filter <beginfold id='1'>{</beginfold id='1'> !$0.isEmpty <endfold id='1'>}</endfold id='1'>
        .joined(separator: separator)
<endfold id='1'>}</endfold id='1'>

// Serialize arguments
func serialize(arguments: [String: String],
               separator: String = "\n") -> String
<beginfold id='1'>{</beginfold id='1'>
    return arguments.map <beginfold id='1'>{</beginfold id='1'>
        var value = $1
        if value.contains(" ") <beginfold id='1'>{</beginfold id='1'>
            value = "\"\(value.replacingOccurrences(of: "\"", with: "\\\""))\""
        <endfold id='1'>}</endfold id='1'>
        return "--\($0) \(value)"
    <endfold id='1'>}</endfold id='1'>.sorted().joined(separator: separator)
<endfold id='1'>}</endfold id='1'>

// Get command line arguments from options
func argumentsFor(_ options: Options, excludingDefaults: Bool = false) -> [String: String] <beginfold id='1'>{</beginfold id='1'>
    var args = [String: String]()
    if let fileOptions = options.fileOptions <beginfold id='1'>{</beginfold id='1'>
        var arguments = Set(fileArguments)
        do <beginfold id='1'>{</beginfold id='1'>
            if !excludingDefaults || fileOptions.followSymlinks != FileOptions.default.followSymlinks <beginfold id='1'>{</beginfold id='1'>
                args["symlinks"] = fileOptions.followSymlinks ? "follow" : "ignore"
            <endfold id='1'>}</endfold id='1'>
            arguments.remove("symlinks")
        <endfold id='1'>}</endfold id='1'>
        do <beginfold id='1'>{</beginfold id='1'>
            if !fileOptions.excludedGlobs.isEmpty <beginfold id='1'>{</beginfold id='1'>
                // TODO: find a better alternative to stringifying url
                args["exclude"] = fileOptions.excludedGlobs.map <beginfold id='1'>{</beginfold id='1'> $0.description <endfold id='1'>}</endfold id='1'>.sorted().joined(separator: ",")
            <endfold id='1'>}</endfold id='1'>
            arguments.remove("exclude")
        <endfold id='1'>}</endfold id='1'>
        do <beginfold id='1'>{</beginfold id='1'>
            if !fileOptions.unexcludedGlobs.isEmpty <beginfold id='1'>{</beginfold id='1'>
                // TODO: find a better alternative to stringifying url
                args["unexclude"] = fileOptions.unexcludedGlobs.map <beginfold id='1'>{</beginfold id='1'> $0.description <endfold id='1'>}</endfold id='1'>.sorted().joined(separator: ",")
            <endfold id='1'>}</endfold id='1'>
            arguments.remove("unexclude")
        <endfold id='1'>}</endfold id='1'>
        do <beginfold id='1'>{</beginfold id='1'>
            if !excludingDefaults || fileOptions.minVersion != FileOptions.default.minVersion <beginfold id='1'>{</beginfold id='1'>
                args["minversion"] = fileOptions.minVersion.description
            <endfold id='1'>}</endfold id='1'>
            arguments.remove("minversion")
        <endfold id='1'>}</endfold id='1'>
        assert(arguments.isEmpty)
    <endfold id='1'>}</endfold id='1'>
    if let formatOptions = options.formatOptions <beginfold id='1'>{</beginfold id='1'>
        for descriptor in Descriptors.all where !descriptor.isRenamed <beginfold id='1'>{</beginfold id='1'>
            let value = descriptor.fromOptions(formatOptions)
            guard value != descriptor.fromOptions(.default) ||
                (!excludingDefaults && !descriptor.isDeprecated)
            else <beginfold id='1'>{</beginfold id='1'>
                continue
            <endfold id='1'>}</endfold id='1'>
            // Special case for swiftVersion
            // TODO: find a better solution for this
            if descriptor.argumentName == Descriptors.swiftVersion.argumentName,
               value == Version.undefined.rawValue
            <beginfold id='1'>{</beginfold id='1'>
                continue
            <endfold id='1'>}</endfold id='1'>
            args[descriptor.argumentName] = value
        <endfold id='1'>}</endfold id='1'>
        // Special case for wrapParameters
        let argumentName = Descriptors.wrapParameters.argumentName
        if args[argumentName] == WrapMode.default.rawValue <beginfold id='1'>{</beginfold id='1'>
            args[argumentName] = args[Descriptors.wrapArguments.argumentName]
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    if options.lint <beginfold id='1'>{</beginfold id='1'>
        args["lint"] = ""
    <endfold id='1'>}</endfold id='1'>
    if let rules = options.rules <beginfold id='1'>{</beginfold id='1'>
        let defaultRules = allRules.subtracting(FormatRules.disabledByDefault)

        let enabled = rules.subtracting(defaultRules)
        if !enabled.isEmpty <beginfold id='1'>{</beginfold id='1'>
            args["enable"] = enabled.sorted().joined(separator: ",")
        <endfold id='1'>}</endfold id='1'>

        let disabled = defaultRules.subtracting(rules)
        if !disabled.isEmpty <beginfold id='1'>{</beginfold id='1'>
            args["disable"] = disabled.sorted().joined(separator: ",")
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    return args
<endfold id='1'>}</endfold id='1'>

private func processOption(_ key: String,
                           in args: [String: String],
                           from: inout Set<String>,
                           handler: (String) throws -> Void) throws
<beginfold id='1'>{</beginfold id='1'>
    precondition(optionsArguments.contains(key), "\(key) not in optionsArguments")
    var arguments = from
    arguments.remove(key)
    from = arguments
    guard let value = args[key] else <beginfold id='1'>{</beginfold id='1'>
        return
    <endfold id='1'>}</endfold id='1'>
    do <beginfold id='1'>{</beginfold id='1'>
        try handler(value)
    <endfold id='1'>}</endfold id='1'> catch <beginfold id='1'>{</beginfold id='1'>
        guard !value.isEmpty else <beginfold id='1'>{</beginfold id='1'>
            throw FormatError.options("--\(key) option expects a value")
        <endfold id='1'>}</endfold id='1'>
        if case var FormatError.options(string) = error, !string.isEmpty <beginfold id='1'>{</beginfold id='1'>
            if !string.contains(key) <beginfold id='1'>{</beginfold id='1'>
                string += " in --\(key)"
            <endfold id='1'>}</endfold id='1'>
            throw FormatError.options(string)
        <endfold id='1'>}</endfold id='1'>
        throw FormatError.options("Unsupported --\(key) value '\(value)'")
    <endfold id='1'>}</endfold id='1'>
<endfold id='1'>}</endfold id='1'>

// Parse rule names from arguments
public func rulesFor(_ args: [String: String], lint: Bool) throws -> Set<String> <beginfold id='1'>{</beginfold id='1'>
    var rules = allRules
    rules = try args["rules"].map <beginfold id='1'>{</beginfold id='1'>
        try Set(parseRules($0))
    <endfold id='1'>}</endfold id='1'> ?? rules.subtracting(FormatRules.disabledByDefault)
    try args["enable"].map <beginfold id='1'>{</beginfold id='1'>
        try rules.formUnion(parseRules($0))
    <endfold id='1'>}</endfold id='1'>
    try args["disable"].map <beginfold id='1'>{</beginfold id='1'>
        try rules.subtract(parseRules($0))
    <endfold id='1'>}</endfold id='1'>
    try args["lintonly"].map <beginfold id='1'>{</beginfold id='1'> rulesString in
        if lint <beginfold id='1'>{</beginfold id='1'>
            try rules.formUnion(parseRules(rulesString))
        <endfold id='1'>}</endfold id='1'> else <beginfold id='1'>{</beginfold id='1'>
            try rules.subtract(parseRules(rulesString))
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    return rules
<endfold id='1'>}</endfold id='1'>

// Parse FileOptions from arguments
func fileOptionsFor(_ args: [String: String], in directory: String) throws -> FileOptions? <beginfold id='1'>{</beginfold id='1'>
    var options = FileOptions()
    var arguments = Set(fileArguments)

    var containsFileOption = false
    try processOption("symlinks", in: args, from: &arguments) <beginfold id='1'>{</beginfold id='1'>
        containsFileOption = true
        switch $0.lowercased() <beginfold id='1'>{</beginfold id='1'>
        case "follow":
            options.followSymlinks = true
        case "ignore":
            options.followSymlinks = false
        default:
            throw FormatError.options("")
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    try processOption("exclude", in: args, from: &arguments) <beginfold id='1'>{</beginfold id='1'>
        containsFileOption = true
        options.excludedGlobs += expandGlobs($0, in: directory)
    <endfold id='1'>}</endfold id='1'>
    try processOption("unexclude", in: args, from: &arguments) <beginfold id='1'>{</beginfold id='1'>
        containsFileOption = true
        options.unexcludedGlobs += expandGlobs($0, in: directory)
    <endfold id='1'>}</endfold id='1'>
    try processOption("minversion", in: args, from: &arguments) <beginfold id='1'>{</beginfold id='1'>
        containsFileOption = true
        guard let minVersion = Version(rawValue: $0) else <beginfold id='1'>{</beginfold id='1'>
            throw FormatError.options("Unsupported --minversion value '\($0)'")
        <endfold id='1'>}</endfold id='1'>
        guard minVersion <= Version(stringLiteral: swiftFormatVersion) else <beginfold id='1'>{</beginfold id='1'>
            throw FormatError.options("Project specifies SwiftFormat --minversion of \(minVersion)")
        <endfold id='1'>}</endfold id='1'>
        options.minVersion = minVersion
    <endfold id='1'>}</endfold id='1'>
    assert(arguments.isEmpty, "\(arguments.joined(separator: ","))")
    return containsFileOption ? options : nil
<endfold id='1'>}</endfold id='1'>

// Parse FormatOptions from arguments
// Returns nil if the arguments dictionary does not contain any formatting arguments
public func formatOptionsFor(_ args: [String: String]) throws -> FormatOptions? <beginfold id='1'>{</beginfold id='1'>
    var options = FormatOptions.default
    var arguments = Set(formattingArguments)

    var containsFormatOption = false
    for option in Descriptors.all <beginfold id='1'>{</beginfold id='1'>
        try processOption(option.argumentName, in: args, from: &arguments) <beginfold id='1'>{</beginfold id='1'>
            containsFormatOption = true
            try option.toOptions($0, &options)
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    assert(arguments.isEmpty, "\(arguments.joined(separator: ","))")
    return containsFormatOption ? options : nil
<endfold id='1'>}</endfold id='1'>

// Get deprecation warnings from a set of arguments
func warningsForArguments(_ args: [String: String]) -> [String] <beginfold id='1'>{</beginfold id='1'>
    var warnings = [String]()
    for option in Descriptors.all <beginfold id='1'>{</beginfold id='1'>
        if args[option.argumentName] != nil, let message = option.deprecationMessage <beginfold id='1'>{</beginfold id='1'>
            warnings.append("--\(option.argumentName) option is deprecated. \(message)")
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    for name in Set(rulesArguments.flatMap <beginfold id='1'>{</beginfold id='1'> (try? args[$0].map(parseRules) ?? []) ?? [] <endfold id='1'>}</endfold id='1'>) <beginfold id='1'>{</beginfold id='1'>
        if let message = FormatRules.byName[name]?.deprecationMessage <beginfold id='1'>{</beginfold id='1'>
            warnings.append("\(name) rule is deprecated. \(message)")
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    if let rules = try? rulesFor(args, lint: true) <beginfold id='1'>{</beginfold id='1'>
        for arg in args.keys where formattingArguments.contains(arg) <beginfold id='1'>{</beginfold id='1'>
            if !rules.contains(where: <beginfold id='1'>{</beginfold id='1'>
                guard let rule = FormatRules.byName[$0] else <beginfold id='1'>{</beginfold id='1'>
                    return false
                <endfold id='1'>}</endfold id='1'>
                return rule.options.contains(arg) || rule.sharedOptions.contains(arg)
            <endfold id='1'>}</endfold id='1'>) <beginfold id='1'>{</beginfold id='1'>
                let expected = FormatRules.all.first(where: <beginfold id='1'>{</beginfold id='1'>
                    $0.options.contains(arg)
                <endfold id='1'>}</endfold id='1'>)?.name ?? "associated"
                warnings.append("--\(arg) option has no effect when \(expected) rule is disabled")
            <endfold id='1'>}</endfold id='1'>
        <endfold id='1'>}</endfold id='1'>
    <endfold id='1'>}</endfold id='1'>
    return warnings
<endfold id='1'>}</endfold id='1'>

let fileArguments = [
    "symlinks",
    "exclude",
    "unexclude",
    "minversion",
]

let rulesArguments = [
    "disable",
    "enable",
    "lintonly",
    "rules",
]

let formattingArguments = Descriptors.formatting.map <beginfold id='1'>{</beginfold id='1'> $0.argumentName <endfold id='1'>}</endfold id='1'>
let internalArguments = Descriptors.internal.map <beginfold id='1'>{</beginfold id='1'> $0.argumentName <endfold id='1'>}</endfold id='1'>
let optionsArguments = fileArguments + rulesArguments + formattingArguments + internalArguments

let commandLineArguments = [
    // Input options
    "filelist",
    "stdinpath",
    "config",
    "inferoptions",
    "linerange",
    "output",
    "cache",
    "dryrun",
    "lint",
    "lenient",
    "verbose",
    "quiet",
    "report",
    // Misc
    "help",
    "version",
    "options",
    "ruleinfo",
] + optionsArguments

let deprecatedArguments = Descriptors.all.compactMap <beginfold id='1'>{</beginfold id='1'>
    $0.isDeprecated ? $0.argumentName : nil
<endfold id='1'>}</endfold id='1'>

protocol Foo <beginfold id='1'>{</beginfold id='1'>
    func foo()
    func bar()
<endfold id='1'>}</endfold id='1'>

// something
class FooImpl: Foo <beginfold id='1'>{</beginfold id='1'>
<endfold id='1'>}</endfold id='1'>