-
Notifications
You must be signed in to change notification settings - Fork 207
Improved Ink CLI tool #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Added -h/--help and --version tags Changed functionality of calling ink to make it more testable and permit standard forms of input (pipes, redirections, etc. `$ ink` reads from stdin `$ ink file.md` reads contents of file `$ ink -m string` parses string directly
|
This is in preparation for PR #20 discussing CommonMark testing and benchmarking tools. These changes are necessary in order to support testing and benchmarking using the CommonMark tools (specifically, being able take a filename and parse its contents, read from stdin, and redirection). The upshot is a much better CLI tool, without too much additional complexity, so I separated it into its own PR. The readme and the tool's usage message now provide more examples and documentation. There's probably also pedagogical value in showing how to make a tool that accepts differing forms of command line input. This is actually pretty extensively tested on this branch, but because the tests work by directly calling the build product through bash, they require a symlink to be created before they will pass. In my mind, this makes the tests unsuitable for general inclusion here. Suffice it to say, I'm confident it will work as described. |
JohnSundell
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this @john-mueller, I'm really excited to get the infra needed for CommonMark benchmarking in place 👍 Left some comments inline, let me know what you think. I'd also love to hear what @ezfe and @bamx23 thinks about this slight redesign of the CLI.
Sources/InkCLI/Printing.swift
Outdated
|
|
||
| import Foundation | ||
|
|
||
| internal var versionMessage: String = "Ink (v0.1.2): Markdown -> HTML converter" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless we automate this, there's a very little chance that it'll stay up to date, since it'd require manually modifying the source code each time I make a new release. I suggest dropping it, at least for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I thought of that. Not a big deal to omit.
Sources/InkCLI/Printing.swift
Outdated
|
|
||
| internal enum Output { case standardOut, standardError } | ||
|
|
||
| internal func print(_ message: String, on output: Output) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of introducing a new print function, why not call fputs directly from main.swift? And if you wish to wrap it, how about creating a dedicated printError(_ error:) function instead, since Swift.print doesn't need wrapping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed so only fputs is wrapped. I like the printError wrapper, as fputs doesn't append a newline like print, so having parallel calls makes it easier to avoid mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love it!
Sources/InkCLI/main.swift
Outdated
| let markdown: String | ||
|
|
||
| if markdown == "-" { | ||
| if CommandLine.arguments.count == 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be implemented in a very clean way using pattern matching:
let arguments = CommandLine.arguments
switch arguments.count {
case 1:
// parse stdin
case 2:
// parse file
case 3 where arguments[1] == "-m" || arguments[1] == "--markdown":
// parse inline markdown
default:
// error handling
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much cleaner. I left the -m flag check as the second case, so that if you run ink -m or ink -m string1 string2 you get an error message specific to the -m flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the new implementation looks fantastic 👍
Sources/InkCLI/main.swift
Outdated
| do { | ||
| // this is 5x more efficient than 'let data = try String(contentsOf: fileUrl)' | ||
| let data = try Data(contentsOf: fileUrl) | ||
| guard let string = String(data: data, encoding: .utf8) else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be implemented as String(decoding: data, as: UTF8.self) to avoid an optional.
Sources/InkCLI/main.swift
Outdated
| let parser = MarkdownParser() | ||
| print(parser.html(from: markdown)) | ||
| print(parser.html(from: markdown), on: .standardOut) | ||
| exit(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exit(0) is implicit at the end of a program, no need to specify it.
bamx23
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! That's what I was initially thinking about.
Sources/InkCLI/main.swift
Outdated
| case 2: | ||
| // single argument, parse contents of file | ||
| let fileUrl: URL | ||
| if CommandLine.arguments[1].hasPrefix("/") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should also check for ~ and replace it with a home directory path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea! There's an NSString API for that:
NSString(string: CommandLine.arguments[1]).expandingTildeInPathAnd since we depend on Foundation for the CLI, it's no problem doing that bridging IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. Bash was expanding ~ to an absolute string, so ink ~/file.md would parse correctly through the CLI's absolute path, but ink "~/file.md" would look for /$PWD/~/file.md. Doesn't hurt to add a case for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really great attention to detail 👍
Sources/InkCLI/main.swift
Outdated
| } catch { | ||
| printError(error.localizedDescription) | ||
| printError(usageMessage) | ||
| exit(2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about making an enum with exit codes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading this, I think we should either use exit(1) for all errors with no enum, or start error numbering from 3 with an enum. What do you think about the case names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think exit(1) is just fine. Or is it planned to use these exit codes somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think exit(1) would work fine, as it's not really used anywhere, and it reduces code. I originally had numbered them sequentially before really reading anything about standards for error codes. Especially since the user already gets a error message describing the issue, multiple exit codes seems redundant.
Sources/InkCLI/ExitCode.swift
Outdated
| enum ExitCode: Int32 { | ||
| case normal = 0 | ||
| case badMarkdownFlagUsage = 3 | ||
| case problemReadingFile |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it would be better to explicitly write every code number here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind
JohnSundell
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Thanks so much for this @john-mueller, merging this in, and will take a look at your second PR as well either today or tomorrow 👍 Really appreciate your work on this.
Added -h/--help and --version tags
Changed functionality of calling ink to make it more testable and permit standard forms of input (pipes, redirections, etc.)
$ inkreads from stdin$ ink file.mdreads contents of file$ ink -m stringparses string directly