A Quick Swift Script Yak Shave
[Updated Version with downloadable OS X service]
In the process of writing a longer post yesterday, I wanted to add a markdown table, and was once again annoyed at the mild PITA that it is to use pipe characters and then the formatting characters a lá:
| title | title2 |
| :---------: | :--------: |
| entry one | entry two |
| entry three | entry four |
Being aware of Brett Terpstra’s excellent [Markdown Services][markdownServices] that have a table cleanup function, but actually writing the da** tables is kind of annoying, so I in classic [Yak-Shaving][yakShave] fashion, I decided to write a simple [Swift script][OSXScripts] that would turn the following into the format above. Then make that into an [OS X service][automatorServices].
title, title2
entry one, entry two
entry three, entry four
It really wasn’t that tricky, and probably would have been at least as easy using python or ruby, but ¯_(ツ)_/¯. (<– typed using textexpander).
It’s written in Swift 3. I should use stdin
and stdout
, but the syntax for NSFileHandler
changed enough, and I know so little of it, that I decided to punt on that, as I didn’t need the Yak that well shaven. Fortunately the readline
function in a while
loop works just fine and simply print(output)
also works. Likewise the code is naive, could be more functional, does not handle many (any) exceptional cases or check for consistency, but again, it does the job for a simple case. (You know, I’m embarrassed for anyone to actually look at my code)
Likewise, I would like to add a small tweak so that I can optionally write it this way to add the alignment more easily:
title, title2, title3
>, -, <
entry one, entry two, entry three
entry four, entry five, entry six
that would translate the second line to:
| ---: | :---: | :--- |
But Yak-Shaving is done for now.
Entire script is here:
#!/usr/bin/swift
//
// main.swift
// CSVToTables
//
// Created by Christopher Brandow on 7/12/16.
// Copyright © 2016 flouu. All rights reserved.
//
import Foundation
func titleAndEntryFrom(string: String) -> (String, [String])? {
var lines = string.characters.split{$0 == "\n"}.map(String.init)
guard lines.count > 1, let titleLine = lines.first else {
return nil
}
lines.removeFirst()
return (titleLine, lines)
}
func markdownTableFromTitleAndEntries(title: String, body: [String]) -> String? {
let titleMarkdown = markdownFrom(line: title)
let entries = title.characters.split{$0 == ","}.map(String.init)
var separatorMarkdown = "\n|"
for _ in 0..<entries.count {
separatorMarkdown += " :---: |"
}
var bodyMarkdown = ""
for line in body {
bodyMarkdown += markdownFrom(line: line)
}
return titleMarkdown + separatorMarkdown + bodyMarkdown
}
func markdownFrom(line: String) -> String {
var markdown = "\n| "
let entries = line.characters.split{$0 == ","}.map(String.init)
for entry in entries {
markdown += entry
markdown += " |"
}
return markdown
}
var string = ""
while let thing = readLine(strippingNewline: false) {
string += thing
}
if let titleAndBody = titleAndEntryFrom(string: string) {
if let output = markdownTableFromTitleAndEntries(title: titleAndBody.0, body: titleAndBody.1) {
print(output)
}
}
[markdownServices]: http://brettterpstra.com/projects/markdown-service-tools/
[OSXScripts]: http://krakendev.io/blog/scripting-in-swift
[automatorServices]: http://arstechnica.com/apple/2011/03/howto-build-mac-os-x-services-with-automator-and-shell-scripting/
[yakShave]: http://www.hanselman.com/blog/YakShavingDefinedIllGetThatDoneAsSoonAsIShaveThisYak.aspx