diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09b69a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin/ +mdview +mdview.exe diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f8b026b --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +default: all +all: linux windows darwin freebsd + +linux: bin/linux-amd64/mdview +windows: bin/windows-amd64/mdview.exe +darwin: bin/darwin-amd64/mdview +freebsd: bin/freebsd-amd64/mdview + +bin/linux-amd64/mdview: + env GOOS=linux GOARCH=amd64 go build -o ./bin/linux-amd64/mdview +bin/windows-amd64/mdview.exe: + env GOOS=windows GOARCH=amd64 go build -o ./bin/windows-amd64/mdview.exe +bin/darwin-amd64/mdview: + env GOOS=darwin GOARCH=amd64 go build -o ./bin/darwin-amd64/mdview +bin/freebsd-amd64/mdview: + env GOOS=freebsd GOARCH=amd64 go build -o ./bin/freebsd-amd64/mdview + +.PHONY: clean +clean: + rm -rf ./bin diff --git a/README.md b/README.md new file mode 100644 index 0000000..6cde343 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Markdown View + +Formats markdown and launches it in a browser. + +## Install + +If you have Go installed... +```sh +go get github.com/mapitman/mdview +``` + +## Usage + +```sh +mdview [options] +Formats markdown and launches it in a browser. + -o string + Output filename. (Optional) +``` + +If you do not supply an output file, mdview will write a file to your +operating system's default temp directory. diff --git a/main.go b/main.go new file mode 100644 index 0000000..890157f --- /dev/null +++ b/main.go @@ -0,0 +1,169 @@ +package main + +import ( + "crypto/rand" + "encoding/hex" + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/golang-commonmark/markdown" + "github.com/pkg/browser" +) + +func main() { + outfilePtr := flag.String("o", "", "Output filename. (Optional)") + flag.Parse() + inputFilename := flag.Arg(0) + + if inputFilename == "" { + os.Stderr.WriteString("Usage:\nmdview [options] \nFormats markdown and launches it in a browser.\n") + flag.PrintDefaults() + os.Exit(1) + } + + dat, err := ioutil.ReadFile(inputFilename) + check(err) + + md := markdown.New(markdown.XHTMLOutput(true), markdown.Nofollow(true)) + html := md.RenderToString([]byte(dat)) + + outfilePath := *outfilePtr + if outfilePath == "" { + outfilePath = tempFileName("mdview", ".html") + } + + f, err := os.Create(outfilePath) + check(err) + defer f.Close() + _, err = fmt.Fprintf(f, template, style, html) + check(err) + f.Sync() + err = browser.OpenFile(outfilePath) + check(err) +} + +func tempFileName(prefix, suffix string) string { + randBytes := make([]byte, 16) + rand.Read(randBytes) + return filepath.Join(os.TempDir(), prefix+hex.EncodeToString(randBytes)+suffix) +} + +func check(e error) { + if e != nil { + panic(e) + } +} + +const template = "%s" + +const style = `.markdown-body {box-sizing: border-box;min-width: 200px;max-width: + 980px;margin: 0 auto;padding: 45px;} @media (max-width: 767px) {.markdown-body + {padding: 15px;}}.markdown-body hr::after,.markdown-body::after{clear:both} + @font-face{font-family:octicons-link;src:url(data:font/woff;charset=utf-8; + base64,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) + format('woff')}.markdown-body{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%; + color:#24292e;font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\", + Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\" + ;font-size:16px;line-height:1.5;word-wrap:break-word}.markdown-body .pl-c{color:#6a737d} + .markdown-body .pl-c1,.markdown-body .pl-s .pl-v{color:#005cc5}.markdown-body .pl-e, + .markdown-body .pl-en{color:#6f42c1}.markdown-body .pl-s .pl-s1,.markdown-body .pl-smi{color:#24292e} + .markdown-body .pl-ent{color:#22863a}.markdown-body .pl-k{color:#d73a49}.markdown-body .pl-pds, + .markdown-body .pl-s,.markdown-body .pl-s .pl-pse .pl-s1,.markdown-body .pl-sr,.markdown-body + .pl-sr .pl-cce,.markdown-body .pl-sr .pl-sra,.markdown-body .pl-sr .pl-sre{color:#032f62}.markdown-body + .pl-smw,.markdown-body .pl-v{color:#e36209}.markdown-body .pl-bu{color:#b31d28}.markdown-body + .pl-ii{color:#fafbfc;background-color:#b31d28}.markdown-body .pl-c2{color:#fafbfc;background-color:#d73a49} + .markdown-body .pl-c2::before{content:\"^M\"}.markdown-body .pl-sr .pl-cce{font-weight:700;color:#22863a} + .markdown-body .pl-ml{color:#735c0f}.markdown-body .pl-mh,.markdown-body .pl-mh .pl-en,.markdown-body + .pl-ms{font-weight:700;color:#005cc5}.markdown-body .pl-mi{font-style:italic;color:#24292e}.markdown-body + .pl-mb{font-weight:700;color:#24292e}.markdown-body .pl-md{color:#b31d28;background-color:#ffeef0} + .markdown-body .pl-mi1{color:#22863a;background-color:#f0fff4}.markdown-body .pl-mc{color:#e36209; + background-color:#ffebda}.markdown-body .pl-mi2{color:#f6f8fa;background-color:#005cc5} + .markdown-body .pl-mdr{font-weight:700;color:#6f42c1}.markdown-body .pl-ba{color:#586069} + .markdown-body .pl-sg{color:#959da5}.markdown-body .pl-corl{text-decoration:underline; + color:#032f62}.markdown-body .octicon{display:inline-block;fill:currentColor;vertical-align:text-bottom} + .markdown-body hr::after,.markdown-body hr::before,.markdown-body::after, + .markdown-body::before{display:table;content:\"\"}.markdown-body a{background-color:transparent; + color:#0366d6;text-decoration:none}.markdown-body a:active,.markdown-body a:hover{outline-width:0} + .markdown-body h1{margin:.67em 0}.markdown-body img{border-style:none}.markdown-body hr{box-sizing:content-box} + .markdown-body input{font:inherit;margin:0;overflow:visible;font-family:inherit;font-size:inherit;line-height:inherit} + .markdown-body dl dt,.markdown-body strong,.markdown-body table th{font-weight:600}.markdown-body code, + .markdown-body pre{font-family:SFMono-Regular,Consolas,\"Liberation Mono\",Menlo,Courier,monospace} + .markdown-body [type=checkbox]{box-sizing:border-box;padding:0}.markdown-body *{box-sizing:border-box} + .markdown-body a:hover{text-decoration:underline}.markdown-body td,.markdown-body th{padding:0} + .markdown-body blockquote{margin:0}.markdown-body ol ol,.markdown-body ul ol{list-style-type:lower-roman} + .markdown-body ol ol ol,.markdown-body ol ul ol,.markdown-body ul ol ol,.markdown-body + ul ul ol{list-style-type:lower-alpha}.markdown-body dd{margin-left:0}.markdown-body + .pl-0{padding-left:0!important}.markdown-body .pl-1{padding-left:4px!important}.markdown-body + .pl-2{padding-left:8px!important}.markdown-body .pl-3{padding-left:16px!important}.markdown-body + .pl-4{padding-left:24px!important}.markdown-body .pl-5{padding-left:32px!important}.markdown-body + .pl-6{padding-left:40px!important}.markdown-body>:first-child{margin-top:0!important} + .markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit; + text-decoration:none}.markdown-body .anchor{float:left;padding-right:4px;margin-left:-20px; + line-height:1}.markdown-body .anchor:focus{outline:0}.markdown-body blockquote, + .markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table, + .markdown-body ul{margin-top:0;margin-bottom:16px}.markdown-body hr{overflow:hidden;background:#e1e4e8; + height:.25em;padding:0;margin:24px 0;border:0}.markdown-body blockquote{padding:0 1em;color:#6a737d; + border-left:.25em solid #dfe2e5}.markdown-body h1,.markdown-body h2{padding-bottom:.3em; + border-bottom:1px solid #eaecef}.markdown-body blockquote>:first-child{margin-top:0} + .markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body h1,.markdown-body h2, + .markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{margin-top:24px; + margin-bottom:16px;font-weight:600;line-height:1.25}.markdown-body h1 .octicon-link,.markdown-body + h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body + h5 .octicon-link,.markdown-body h6 .octicon-link{color:#1b1f23;vertical-align:middle;visibility:hidden} + .markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor, + .markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none} + .markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body + h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor + .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1{font-size:2em} + .markdown-body h2{font-size:1.5em}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body + h5{font-size:.875em}.markdown-body h6{font-size:.85em;color:#6a737d}.markdown-body ol,.markdown-body ul{padding-left:2em} + .markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:0;margin-bottom:0} + .markdown-body li{word-wrap:break-all}.markdown-body li>p{margin-top:16px}.markdown-body li+li{margin-top:.25em} + .markdown-body dl{padding:0}.markdown-body dl dt{padding:0;margin-top:16px;font-size:1em;font-style:italic}.markdown-body + dl dd{padding:0 16px;margin-bottom:16px}.markdown-body table{border-spacing:0;border-collapse:collapse;display:block; + width:100%;overflow:auto}.markdown-body table td,.markdown-body table th{padding:6px 13px;border:1px solid #dfe2e5} + .markdown-body table tr{background-color:#fff;border-top:1px solid #c6cbd1}.markdown-body table + tr:nth-child(2n){background-color:#f6f8fa}.markdown-body img{max-width:100%;box-sizing:content-box;background-color:#fff} + .markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body + code{padding:.2em .4em;margin:0;font-size:85%;background-color:rgba(27,31,35,.05);border-radius:3px}.markdown-body + pre{word-wrap:normal}.markdown-body pre>code{padding:0;margin:0;font-size:100%;word-break:normal;white-space:pre;background:0 0; + border:0}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal} + .markdown-body .highlight pre,.markdown-body pre{padding:16px;overflow:auto;font-size:85%;line-height:1.45; + background-color:#f6f8fa;border-radius:3px}.markdown-body pre code{display:inline;max-width:auto;padding:0;margin:0; + overflow:visible;line-height:inherit;word-wrap:normal;background-color:transparent;border:0}.markdown-body + .full-commit .btn-outline:not(:disabled):hover{color:#005cc5;border-color:#005cc5}.markdown-body kbd{display:inline-block; + padding:3px 5px;font:11px SFMono-Regular,Consolas,\"Liberation Mono\",Menlo,Courier,monospace;line-height:10px;color:#444d56; + vertical-align:middle;background-color:#fafbfc;border:1px solid #d1d5da;border-bottom-color:#c6cbd1;border-radius:3px; + box-shadow:inset 0 -1px 0 #c6cbd1}.markdown-body :checked+.radio-label{position:relative;z-index:1;border-color:#0366d6} + .markdown-body .task-list-item{list-style-type:none}.markdown-body .task-list-item+.task-list-item{margin-top:3px} + .markdown-body .task-list-item input{margin:0 .2em .25em -1.6em;vertical-align:middle}.markdown-body hr{border-bottom-color:#eee}`