11# Swift CommandLineKit
22
33[ ![ Platform: macOS] ( https://img.shields.io/badge/Platform-macOS-blue.svg?style=flat )] ( https://developer.apple.com/osx/ )
4+ [ ![ Platform: Linux] ( https://img.shields.io/badge/Platform-Linux-blue.svg?style=flat )] ( https://www.ubuntu.com/ )
45[ ![ Language: Swift 4.1] ( https://img.shields.io/badge/Language-Swift%204.1-green.svg?style=flat )] ( https://developer.apple.com/swift/ )
56[ ![ IDE: Xcode 9.3] ( https://img.shields.io/badge/IDE-Xcode%209.3-orange.svg?style=flat )] ( https://developer.apple.com/xcode/ )
67[ ![ Carthage: compatible] ( https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat )] ( https://github.com/Carthage/Carthage )
@@ -18,8 +19,184 @@ functionality:
1819 based on the library [ Linenoise-Swift] ( https://github.com/andybest/linenoise-swift ) ,
1920 but supporting unicode input, multiple lines, and styled text.
2021
22+ ## Command-line arguments
23+
24+ ### Basics
25+
26+ CommandLineKit handles command-line arguments with the following protocol:
27+
28+ 1 . A new [ Flags] ( https://github.com/objecthub/swift-commandlinekit/blob/master/Sources/CommandLineKit/Flags.swift )
29+ object gets created either for the system-provided command-line arguments or for a
30+ custom sequence of arguments.
31+ 2 . For every flag, a [ Flag] ( https://github.com/objecthub/swift-commandlinekit/blob/master/Sources/CommandLineKit/Flag.swift )
32+ object is being created and registered in the ` Flags ` object.
33+ 3 . Once all flag objects are declared and registered, the command-line gets parsed. After parsing
34+ is complete, the flag objects can be used to access the extracted options and arguments.
35+
36+ CommandLineKit defines different types of
37+ [ Flag] ( https://github.com/objecthub/swift-commandlinekit/blob/master/Sources/CommandLineKit/Flag.swift )
38+ subclasses for handling _ options_ (i.e. flags without
39+ parameters) and _ arguments_ (i.e. flags with parameters). Arguments are either _ singleton arguments_ (i.e. they
40+ have exactly one value) or they are _ repeated arguments_ (i.e. they have many values). Arguments are
41+ parameterized with a type which defines how to parse values. The framework natively supports _ int_ ,
42+ _ double_ , _ string_ , and _ enum_ types, which means that in practice, just using the built-in flag classes
43+ are almost always sufficient. Nevertheless,
44+ [ the framework is extensible] ( https://github.com/objecthub/swift-commandlinekit/tree/master/Sources/CommandLineKit )
45+ and supports arbitrary argument types.
46+
47+ A flag is identified by a _ short name_ character and a _ long name_ string. At least one of the two needs to be
48+ defined. For instance, the "help" option could be defined by the short name "h" and the long name "help".
49+ On the command-line, a user could either use ` -h ` or ` --help ` to refer to this option; i.e. short names are
50+ prefixed with a single dash, long names are prefixed with a double dash.
51+
52+ An argument is a parameterized flag. The parameters follow directly the flag identifier (typically separated by
53+ a space). For instance, an integer argument with long name "size" could be defined as: ` --size 64 ` . If the
54+ argument is repeated, then multiple parameters may follow the flag identifier, as in this
55+ example: ` --size 2 4 8 16 ` . The sequence is terminated by either the end of the command-line arguments,
56+ another flag, or the terminator "---". All command-line arguments following the terminator are not being parsed
57+ and are returned in the ` parameters ` field of the ` Flags ` object.
58+
59+ ### Example
60+
61+ Here is an [ example] ( https://github.com/objecthub/swift-lispkit/blob/master/Sources/LispKitRepl/main.swift )
62+ from the [ LispKit] ( https://github.com/objecthub/swift-lispkit ) project. It uses factory methods (like ` flags.string ` ,
63+ ` flags.int ` , ` flags.option ` , ` flags.strings ` , etc.) provided by the
64+ [ Flags] ( https://github.com/objecthub/swift-commandlinekit/blob/master/Sources/CommandLineKit/Flags.swift )
65+ class to create and register individual flags.
66+
67+ ``` swift
68+ // Create a new flags object for the system-provided command-line arguments
69+ var flags = Flags ()
70+
71+ // Define the various flags
72+ let filePaths = flags.strings (" f" , " filepath" ,
73+ description : " Adds file path in which programs are searched for." )
74+ let libPaths = flags.strings (" l" , " libpath" ,
75+ description : " Adds file path in which libraries are searched for." )
76+ let heapSize = flags.int (" h" , " heapsize" ,
77+ description : " Initial capacity of the heap" , value : 1000 )
78+ let importLibs = flags.strings (" i" , " import" ,
79+ description : " Imports library automatically after startup." )
80+ let prelude = flags.string (" p" , " prelude" ,
81+ description : " Path to prelude file which gets executed after " +
82+ " loading all provided libraries." )
83+ let prompt = flags.string (" r" , " prompt" ,
84+ description : " String used as prompt in REPL." , value : AppInfo.prompt )
85+ let quiet = flags.option (" q" , " quiet" ,
86+ description : " In quiet mode, optional messages are not printed." )
87+ let help = flags.option (" h" , " help" ,
88+ description : " Show description of usage and options of this tools." )
89+
90+ // Parse the command-line arguments and return error message if parsing fails
91+ if let failure = self .parsingFailure () {
92+ print (failure)
93+ exit (1 )
94+ }
95+ ```
96+
97+ The framework supports printing the supported options via the ` Flags.usageDescription ` function. For the
98+ command-line flags as defined above, this function returns the following usage description:
99+
100+ ```
101+ usage: LispKitRepl [<option> ...] [---] [<program> <arg> ...]
102+ options:
103+ -f, --filepath <value> ...
104+ Adds file path in which programs are searched for.
105+ -l, --libpath <value> ...
106+ Adds file path in which libraries are searched for.
107+ -h, --heapsize <value>
108+ Initial capacity of the heap
109+ -i, --import <value> ...
110+ Imports library automatically after startup.
111+ -p, --prelude <value>
112+ Path to prelude file which gets executed after loading all provided libraries.
113+ -r, --prompt <value>
114+ String used as prompt in REPL.
115+ -q, --quiet
116+ In quiet mode, optional messages are not printed.
117+ -h, --help
118+ Show description of usage and options of this tools.
119+ ```
120+
121+ Command-line tools can inspect whether a flag was set via the ` Flag.wasSet ` field. For flags with
122+ parameters, the parameters are stored in the ` Flag.value ` field. The type of this field is dependent on the
123+ flag type. For repeated flags, an array is used.
124+
125+
126+ ## Text style and colors
127+
128+ CommandLineKit provides a
129+ [ TextProperties] ( https://github.com/objecthub/swift-commandlinekit/blob/master/Sources/CommandLineKit/TextProperties.swift )
130+ structure for bundling a text color, a background color, and a text style in a single object. Text properties can be
131+ merged with the ` with(:) ` functions and applied to a string with the ` apply(to:) ` function.
132+
133+ Individual enumerations for
134+ [ TextColor] ( https://github.com/objecthub/swift-commandlinekit/blob/master/Sources/CommandLineKit/TextColor.swift ) ,
135+ [ BackgroundColor] ( https://github.com/objecthub/swift-commandlinekit/blob/master/Sources/CommandLineKit/BackgroundColor.swift ) , and
136+ [ TextStyle] ( https://github.com/objecthub/swift-commandlinekit/blob/master/Sources/CommandLineKit/TextStyle.swift )
137+ define the individual properties.
138+
139+ ## Reading strings
140+
141+ CommandLineKit includes a significantly improved version of the "readline" API originally defined by the library
142+ [ Linenoise-Swift] ( https://github.com/andybest/linenoise-swift ) . It supports unicode text, multi-line text entry, and
143+ styled text. It supports all the existing features such as _ advanced keyboard support_ , _ history_ ,
144+ _ text completion_ , and _ hints_ .
145+
146+ The following code illustrates the usage of the LineReader API:
147+
148+ ```
149+ if let ln = LineReader() {
150+ ln.setCompletionCallback { currentBuffer in
151+ let completions = [
152+ "Hello!",
153+ "Hello Google",
154+ "Scheme is awesome!"
155+ ]
156+ return completions.filter { $0.hasPrefix(currentBuffer) }
157+ }
158+ ln.setHintsCallback { currentBuffer in
159+ let hints = [
160+ "Foo",
161+ "Lorem Ipsum",
162+ "Scheme is awesome!"
163+ ]
164+ let filtered = hints.filter { $0.hasPrefix(currentBuffer) }
165+ if let hint = filtered.first {
166+ let hintText = String(hint.dropFirst(currentBuffer.count))
167+ return (hintText, TextColor.grey.properties)
168+ } else {
169+ return nil
170+ }
171+ }
172+ print("Type 'exit' to quit")
173+ var done = false
174+ while !done {
175+ do {
176+ let output = try ln.readLine(prompt: "> ",
177+ maxCount: 200,
178+ strippingNewline: true,
179+ promptProperties: TextProperties(.green, nil, .bold),
180+ readProperties: TextProperties(.blue, nil),
181+ parenProperties: TextProperties(.red, nil, .bold))
182+ print("Entered: \(output)")
183+ ln.addHistory(output)
184+ if output == "exit" {
185+ break
186+ }
187+ } catch LineReaderError.CTRLC {
188+ print("\nCaptured CTRL+C. Quitting.")
189+ done = true
190+ } catch {
191+ print(error)
192+ }
193+ }
194+ }
195+ ```
196+
21197## Requirements
22198
23- - XCode 9.3
24- - Swift 4.1
25- - Carthage or Swift Package Manager
199+ - [ Xcode 9.3] ( https://developer.apple.com/xcode/ )
200+ - [ Swift 4.1] ( https://developer.apple.com/swift/ )
201+ - [ Carthage] ( https://github.com/Carthage/Carthage )
202+ - [ Swift Package Manager] ( https://swift.org/package-manager/ )
0 commit comments