Golang and the Clipboard (mdclip)
I recently wrote a couple of lines of Go again, mainly to see if I could write a cross platform clone of a two line shell script in Go (which actually makes it like 69 lines, but whatever).
So I wrote mdclip
to compile a file from markdown to HTML and put it in my clipboard (so I can paste it into my WordPress editor). Now you could scroll down to the comment box and go like
USE HUGO
or
USE GHOST
or
STOP BLOGGING
and I will stare down into your face and whisper
No.
(watch Watchmen)
Regardless, it was fun to write the little thing again and I actually learned how to use already available binaries on a system with Go.
As many, I am cursed to live my daily life in a world of multiple operating systems, each with their respective… charms and disgusting little pits of dysfunctionality.
Actually I shamelessly stole the part that deals with figuring out the right command for the clipboard from github.com/atotto/clipboard. Fortunately Mac OS has pbcopy
and Linux has… well you can install xclip
or xsel
. I’m sure they both have a place in the hearts of two raging and different camps of Linux users with lots of trenches between them.
Oh by the way, (n)vim
is better.
Right, so figuring out the operating system was down to:
var copyCmd *exec.Cmd
// Mac "OS"
if arch == "darwin" {
copyCmd = exec.Command("pbcopy")
}
// Linux
if arch == "linux" {
copyCmd = exec.Command("xclip", "-selection", "c")
}
Which is much less advanced than the cool examples in the before referenced project. Another thing I learned is that there apparently is a type for *exec.Cmd
(seriously these go dudes have a type for everything), which apparently represents a command you execute, which I totally need for this one.
Another thing I learned was that you can start up all kinds of commands and then pipe things to them, like with an actual |
, just controlled by your Go code.
output := blackfriday.MarkdownBasic([]byte(dat))
if _, err := in.Write([]byte(output)); err != nil {
log.Fatal(err)
}
Wohooo, who could want more? Best thing, it actually works! I can actually use it!
One more drink thing! (you should listen to Alestorm)
In order to get niceness like fenced codeblocks with \
you'll need to use
output := blackfriday.MarkdownCommon(input)
instead of
output := blackfriday.MarkdownBasic(input)
Finally, assuming you want to use this command globally like mdclip my-cool-buttpost
you'll need to run go install
in the directory where your project lives and where it has a main.go
file.
Here's the full code:
package main
import (
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"github.com/russross/blackfriday"
)
func main() {
arch := runtime.GOOS
if len(os.Args) != 2 {
log.Fatal("Usage: mdclip filename.md")
}
arg := os.Args[1]
fp, err := filepath.Abs(arg)
if err != nil {
log.Fatal(err)
}
dat, err := ioutil.ReadFile(fp)
if err != nil {
log.Fatal(err)
}
output := blackfriday.MarkdownCommon([]byte(dat))
toClipboard(output, arch)
}
func toClipboard(output []byte, arch string) {
var copyCmd *exec.Cmd
// Mac "OS"
if arch == "darwin" {
copyCmd = exec.Command("pbcopy")
}
// Linux
if arch == "linux" {
copyCmd = exec.Command("xclip", "-selection", "c")
}
in, err := copyCmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
if err := copyCmd.Start(); err != nil {
log.Fatal(err)
}
if _, err := in.Write([]byte(output)); err != nil {
log.Fatal(err)
}
if err := in.Close(); err != nil {
log.Fatal(err)
}
copyCmd.Wait()
}