| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- package main
- import (
- "bytes"
- "flag"
- "fmt"
- "html/template"
- "io"
- "os"
- "os/exec"
- "runtime"
- "time"
- "github.com/microcosm-cc/bluemonday"
- "github.com/russross/blackfriday/v2"
- )
- const (
- defaultTemplate = `<!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="content-type" content="text/html; charset=utf-8">
- <title>{{ .Title }}</title>
- </head>
- <body>
- {{ .Body }}
- </body>
- </html>
- `
- )
- type content struct {
- Title string
- Body template.HTML
- }
- func main() {
- // Parse flags
- filename := flag.String("file", "", "Markdown file to preview")
- skipPreview := flag.Bool("s", false, "Skip auto-preview")
- tFname := flag.String("t", "", "Template file to use")
- flag.Parse()
- // If file is not provided, print usage and exit
- if *filename == "" {
- flag.Usage()
- os.Exit(1)
- }
- if err := run(*filename, *tFname, os.Stdout, *skipPreview); err != nil {
- fmt.Fprintf(os.Stderr, "error: %v\n", err)
- os.Exit(1)
- }
- }
- func run(filename string, tFname string, out io.Writer, skipPreview bool) error {
- // Read all the data from the input file and check for errors
- input, err := os.ReadFile(filename)
- if err != nil {
- return fmt.Errorf("could not read file: %w", err)
- }
- // Converting Markdown to HTML
- htmlData, err := parseContent(input, tFname)
- if err != nil {
- return err
- }
- // Create the output file name
- temp, err := os.CreateTemp("", "mdp*.html")
- if err != nil {
- return err
- }
- if err := temp.Close(); err != nil {
- return err
- }
- outName := temp.Name()
- fmt.Fprintln(out, outName)
- if err := saveHTML(outName, htmlData); err != nil {
- return err
- }
- if skipPreview {
- return nil
- }
- defer os.Remove(outName)
- return preview(outName)
- }
- func parseContent(input []byte, tFname string) ([]byte, error) {
- // Parse the markdown file through blackfriday and bluemonday
- // to generate a valid and safe html
- output := blackfriday.Run(input)
- body := bluemonday.UGCPolicy().SanitizeBytes(output)
- // Parse the contents of the defaultTemplate const into a new template
- t, err := template.New("mdp").Parse(defaultTemplate)
- if err != nil {
- return nil, err
- }
- // If user provided alternate template file, replace template
- if tFname != "" {
- t, err = template.ParseFiles(tFname)
- if err != nil {
- return nil, err
- }
- }
- // Initialize the content struct with the title and body
- c := content{
- Title: "Markdown Preview Tool",
- Body: template.HTML(body),
- }
- var buffer bytes.Buffer
- // Execute the template with the content struct and write the output to the buffer
- if err := t.Execute(&buffer, c); err != nil {
- return nil, err
- }
- return buffer.Bytes(), err
- }
- func saveHTML(filename string, data []byte) error {
- return os.WriteFile(filename, data, 0644)
- }
- func preview(fname string) error {
- cName := ""
- cParams := []string{}
- // Define executable based on OS environment
- switch runtime.GOOS {
- case "windows":
- cName = "cmd.exe"
- cParams = []string{"/c", "start"}
- case "linux":
- cName = "xdg-open"
- case "darwin":
- cName = "open"
- default:
- return fmt.Errorf("unsupported platform")
- }
- // Append the filename to the command parameters
- cParams = append(cParams, fname)
- cPath, err := exec.LookPath(cName)
- if err != nil {
- return err
- }
- // Open the file using the default program
- err = exec.Command(cPath, cParams...).Run()
- time.Sleep(2 * time.Second)
- return err
- }
|