All Projects → objecthub → swift-commandlinekit

objecthub / swift-commandlinekit

Licence: BSD-3-Clause license
Framework supporting the development of command-line tools in Swift on macOS and Linux. The framework supports managing command-line arguments, provides lightweight functions to deal with escape sequences, and defines an API for reading strings from the terminal.

Programming Languages

swift
15916 projects
objective c
16641 projects - #2 most used programming language

Projects that are alternatives of or similar to swift-commandlinekit

dr scaffold
scaffold django rest apis like a champion 🚀
Stars: ✭ 116 (+205.26%)
Mutual labels:  command-line-tool
PowerColorLS
PowerShell script to display a colorized directory and file listing with icons
Stars: ✭ 35 (-7.89%)
Mutual labels:  command-line-tool
smartcd
Expedite your navigation of Linux filesystem.
Stars: ✭ 35 (-7.89%)
Mutual labels:  command-line-tool
wifiqr
Create a QR code with your Wi-Fi login details
Stars: ✭ 207 (+444.74%)
Mutual labels:  command-line-tool
hacker
Hack on your project easily. A liftoff proof-of-concept.
Stars: ✭ 21 (-44.74%)
Mutual labels:  flags
content-downloader
Python package to download files on any topic in bulk.
Stars: ✭ 102 (+168.42%)
Mutual labels:  command-line-tool
vim-profiler
A vim plugin profiler and data plotter
Stars: ✭ 31 (-18.42%)
Mutual labels:  command-line-tool
wholesome-cli
Command Line Tool for managing Flutter projects
Stars: ✭ 57 (+50%)
Mutual labels:  command-line-tool
cl-readline
Common Lisp bindings to the GNU Readline library
Stars: ✭ 34 (-10.53%)
Mutual labels:  readline
How-to-use-Readline-in-NodeJS
⌨️ How to manipulate the terminal window using NodeJS
Stars: ✭ 20 (-47.37%)
Mutual labels:  readline
diskspace
macOS command line tool to return the available disk space on APFS volumes
Stars: ✭ 123 (+223.68%)
Mutual labels:  command-line-tool
grift
swift dependency graph visualizer tool
Stars: ✭ 26 (-31.58%)
Mutual labels:  command-line-tool
raisin
A simple lightweight set of implementations and bindings for compression algorithms written in Go.
Stars: ✭ 17 (-55.26%)
Mutual labels:  command-line-tool
dotfiles
dotfiles symbolic links management CLI
Stars: ✭ 156 (+310.53%)
Mutual labels:  command-line-tool
okcli
An Oracle-DB command line client
Stars: ✭ 47 (+23.68%)
Mutual labels:  command-line-tool
audio-playback
Ruby/Command Line Audio File Player
Stars: ✭ 20 (-47.37%)
Mutual labels:  command-line-tool
bdk
Streamlined blockchain deployment kit for Hyperledger Fabric.
Stars: ✭ 43 (+13.16%)
Mutual labels:  command-line-tool
rclc
Mathematical expression calculator with big integers, floats, common fractions, and complex numbers support
Stars: ✭ 24 (-36.84%)
Mutual labels:  command-line-tool
iconset
A nifty command-line tool to customize macOS icons
Stars: ✭ 29 (-23.68%)
Mutual labels:  command-line-tool
WeConnect-cli
Commandline Interface to interact with the Volkswagen WeConnect Services
Stars: ✭ 27 (-28.95%)
Mutual labels:  command-line-tool

Swift CommandLineKit

Platform: macOS Platform: Linux Language: Swift 5.5 IDE: Xcode 13 Carthage: compatible License: BSD

Overview

This is a library supporting the development of command-line tools in the programming language Swift on macOS. It also compiles under Linux. The library provides the following functionality:

  • Management of command-line arguments,
  • Usage of escape sequences on terminals, and
  • Reading strings on terminals using a lineread-inspired implementation based on the library Linenoise-Swift, but supporting unicode input, multiple lines, and styled text.

Command-line arguments

Basics

CommandLineKit handles command-line arguments with the following protocol:

  1. A new Flags object gets created either for the system-provided command-line arguments or for a custom sequence of arguments.
  2. For every flag, a Flag object is being created and registered in the Flags object.
  3. Once all flag objects are declared and registered, the command-line gets parsed. After parsing is complete, the flag objects can be used to access the extracted options and arguments.

CommandLineKit defines different types of Flag subclasses for handling options (i.e. flags without parameters) and arguments (i.e. flags with parameters). Arguments are either singleton arguments (i.e. they have exactly one value) or they are repeated arguments (i.e. they have many values). Arguments are parameterized with a type which defines how to parse values. The framework natively supports int, double, string, and enum types, which means that in practice, just using the built-in flag classes are almost always sufficient. Nevertheless, the framework is extensible and supports arbitrary argument types.

A flag is identified by a short name character and a long name string. At least one of the two needs to be defined. For instance, the "help" option could be defined by the short name "h" and the long name "help". On the command-line, a user could either use -h or --help to refer to this option; i.e. short names are prefixed with a single dash, long names are prefixed with a double dash.

An argument is a parameterized flag. The parameters follow directly the flag identifier (typically separated by a space). For instance, an integer argument with long name "size" could be defined as: --size 64. If the argument is repeated, then multiple parameters may follow the flag identifier, as in this example: --size 2 4 8 16. The sequence is terminated by either the end of the command-line arguments, another flag, or the terminator "---". All command-line arguments following the terminator are not being parsed and are returned in the parameters field of the Flags object.

Example

Here is an example from the LispKit project. It uses factory methods (like flags.string, flags.int, flags.option, flags.strings, etc.) provided by the Flags class to create and register individual flags.

// Create a new flags object for the system-provided command-line arguments
var flags = Flags()

// Define the various flags
let filePaths  = flags.strings("f", "filepath",
                               description: "Adds file path in which programs are searched for.")
let libPaths   = flags.strings("l", "libpath",
                               description: "Adds file path in which libraries are searched for.")
let heapSize   = flags.int("h", "heapsize",
                           description: "Initial capacity of the heap", value: 1000)
let importLibs = flags.strings("i", "import",
                               description: "Imports library automatically after startup.")
let prelude    = flags.string("p", "prelude",
                              description: "Path to prelude file which gets executed after " +
                                           "loading all provided libraries.")
let prompt     = flags.string("r", "prompt",
                              description: "String used as prompt in REPL.", value: "> ")
let quiet      = flags.option("q", "quiet",
                              description: "In quiet mode, optional messages are not printed.")
let help       = flags.option("h", "help",
                              description: "Show description of usage and options of this tools.")

// Parse the command-line arguments and return error message if parsing fails
if let failure = flags.parsingFailure() {
  print(failure)
  exit(1)
}

The framework supports printing the supported options via the Flags.usageDescription function. For the command-line flags as defined above, this function returns the following usage description:

usage: LispKitRepl [<option> ...] [---] [<program> <arg> ...]
options:
  -f, --filepath <value> ...
      Adds file path in which programs are searched for.
  -l, --libpath <value> ...
      Adds file path in which libraries are searched for.
  -h, --heapsize <value>
      Initial capacity of the heap
  -i, --import <value> ...
      Imports library automatically after startup.
  -p, --prelude <value>
      Path to prelude file which gets executed after loading all provided libraries.
  -r, --prompt <value>
      String used as prompt in REPL.
  -q, --quiet
      In quiet mode, optional messages are not printed.
  -h, --help
      Show description of usage and options of this tools.

Command-line tools can inspect whether a flag was set via the Flag.wasSet field. For flags with parameters, the parameters are stored in the Flag.value field. The type of this field is dependent on the flag type. For repeated flags, an array is used.

Here is an example how the flags defined by the code snippet above could be used:

// If help flag was provided, print usage description and exit tool
if help.wasSet {
  print(flags.usageDescription(usageName: TextStyle.bold.properties.apply(to: "usage:"),
                               synopsis: "[<option> ...] [---] [<program> <arg> ...]",
                               usageStyle: TextProperties.none,
                               optionsName: TextStyle.bold.properties.apply(to: "options:"),
                               flagStyle: TextStyle.italic.properties),
        terminator: "")
  exit(0)
}
...
// Define how optional messages and errors are printed
func printOpt(_ message: String) {
  if !quiet.wasSet {
    print(message)
  }
}
...
// Set heap size (assuming 1234 is the default if the flag is not set)
virtualMachine.setHeapSize(heapSize.value ?? 1234)
...
// Register all file paths
for path in filePaths.value {
  virtualMachine.fileHandler.register(path)
}
...
// Load prelude file if it was provided via flag `prelude`
if let file = prelude.value {
  virtualMachine.load(file)
}

Text style and colors

CommandLineKit provides a TextProperties structure for bundling a text color, a background color, and a text style in a single object. Text properties can be merged with the with(:) functions and applied to a string with the apply(to:) function.

Individual enumerations for TextColor, BackgroundColor, and TextStyle define the individual properties.

Reading strings

CommandLineKit includes a significantly improved version of the "readline" API originally defined by the library Linenoise-Swift. It supports unicode text, multi-line text entry, and styled text. It supports all the existing features such as advanced keyboard support, history, text completion, and hints.

The following code illustrates the usage of the LineReader API:

if let ln = LineReader() {
  ln.setCompletionCallback { currentBuffer in
    let completions = [
      "Hello!",
      "Hello Google",
      "Scheme is awesome!"
    ]
    return completions.filter { $0.hasPrefix(currentBuffer) }
  }
  ln.setHintsCallback { currentBuffer in
    let hints = [
      "Foo",
      "Lorem Ipsum",
      "Scheme is awesome!"
    ]
    let filtered = hints.filter { $0.hasPrefix(currentBuffer) }
    if let hint = filtered.first {
      let hintText = String(hint.dropFirst(currentBuffer.count))
      return (hintText, TextColor.grey.properties)
    } else {
      return nil
    }
  }
  print("Type 'exit' to quit")
  var done = false
  while !done {
    do {
      let output = try ln.readLine(prompt: "> ",
                                   maxCount: 200,
                                   strippingNewline: true,
                                   promptProperties: TextProperties(.green, nil, .bold),
                                   readProperties: TextProperties(.blue, nil),
                                   parenProperties: TextProperties(.red, nil, .bold))
      print("Entered: \(output)")
      ln.addHistory(output)
      if output == "exit" {
        break
      }
    } catch LineReaderError.CTRLC {
      print("\nCaptured CTRL+C. Quitting.")
      done = true
    } catch {
      print(error)
    }
  }
}

Requirements

Copyright

Author: Matthias Zenger ([email protected])
Copyright © 2018-2021 Google LLC.
Please note: This is not an official Google product.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].