├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── example.md ├── main.go └── template.html /.gitattributes: -------------------------------------------------------------------------------- 1 | *.md linguist-vendored 2 | *.html linguist-vendored 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 mario 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # markdown-code-highlight-go 2 | 3 | Example of highlighting code from a markdown source in pure go 4 | 5 | Run with 6 | 7 | ```bash 8 | go run main.go > out.html 9 | ``` 10 | -------------------------------------------------------------------------------- /example.md: -------------------------------------------------------------------------------- 1 | Hello 2 | 3 | Some Go Code: 4 | 5 | ```go 6 | if err != nil { 7 | asciilogo() 8 | if os.IsNotExist(err) { 9 | configToSet, configErr := createConfig(config, folder, defaultDBFile) 10 | if configErr != nil { 11 | fatalError(r, configErr) 12 | } 13 | config = []byte(configToSet) 14 | } else { 15 | fatalError(r, fmt.Errorf("error reading config file at %s, %v", configFile, err)) 16 | } 17 | } 18 | ``` 19 | 20 | More Go Code: 21 | 22 | ```go 23 | func setupDB() (*bolt.DB, error) { 24 | db, err := bolt.Open("test.db", 0600, nil) 25 | if err != nil { 26 | return nil, fmt.Errorf("could not open db, %v", err) 27 | } 28 | err = db.Update(func(tx *bolt.Tx) error { 29 | root, err := tx.CreateBucketIfNotExists([]byte("DB")) 30 | if err != nil { 31 | return fmt.Errorf("could not create root bucket: %v", err) 32 | } 33 | _, err = root.CreateBucketIfNotExists([]byte("WEIGHT")) 34 | if err != nil { 35 | return fmt.Errorf("could not create weight bucket: %v", err) 36 | } 37 | _, err = root.CreateBucketIfNotExists([]byte("ENTRIES")) 38 | if err != nil { 39 | return fmt.Errorf("could not create days bucket: %v", err) 40 | } 41 | return nil 42 | }) 43 | if err != nil { 44 | return nil, fmt.Errorf("could not set up buckets, %v", err) 45 | } 46 | fmt.Println("DB Setup Done") 47 | return db, nil 48 | } 49 | ``` 50 | 51 | Some JavaScript Code: 52 | 53 | ```javascript 54 | const Hapi = require('hapi'); 55 | 56 | const server = new Hapi.Server(); 57 | server.connection({ port: 3000, host: 'localhost' }); 58 | 59 | server.route({ 60 | method: 'GET', 61 | path: '/{name}', 62 | handler: function (request, reply) { 63 | reply(`Hello, ${request.params.name}!`); 64 | } 65 | }); 66 | ``` 67 | 68 | And some Java Code: 69 | 70 | ```java 71 | public class WebApp { 72 | public static void main(String[] args) throws IOException { 73 | HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); 74 | server.createContext("/", new LandingPageHandler()); 75 | server.createContext("/post", new PostHandler()); 76 | server.createContext("/json", new JSONHandler()); 77 | server.createContext("/favicon.ico", new IgnoreHandler()); 78 | 79 | server.setExecutor(Executors.newCachedThreadPool()); 80 | server.start(); 81 | 82 | System.out.println("Server started on port 8080" ); 83 | } 84 | } 85 | ``` 86 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "github.com/PuerkitoBio/goquery" 6 | "github.com/russross/blackfriday" 7 | "github.com/sourcegraph/syntaxhighlight" 8 | "html/template" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "strings" 13 | ) 14 | 15 | func main() { 16 | // load markdown file 17 | mdFile, err := ioutil.ReadFile("./example.md") 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | // convert markdown to html 22 | html := blackfriday.MarkdownCommon(mdFile) 23 | // replace code-parts with syntax-highlighted parts 24 | replaced, err := replaceCodeParts(html) 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | // write to stdout 29 | t, err := template.ParseFiles("./template.html") 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | err = t.Execute(os.Stdout, struct{ Content template.HTML }{Content: template.HTML(replaced)}) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | } 38 | 39 | func replaceCodeParts(mdFile []byte) (string, error) { 40 | byteReader := bytes.NewReader(mdFile) 41 | doc, err := goquery.NewDocumentFromReader(byteReader) 42 | if err != nil { 43 | return "", err 44 | } 45 | // find code-parts via css selector and replace them with highlighted versions 46 | doc.Find("code[class*=\"language-\"]").Each(func(i int, s *goquery.Selection) { 47 | oldCode := s.Text() 48 | formatted, err := syntaxhighlight.AsHTML([]byte(oldCode)) 49 | if err != nil { 50 | log.Fatal(err) 51 | } 52 | s.SetHtml(string(formatted)) 53 | }) 54 | new, err := doc.Html() 55 | if err != nil { 56 | return "", err 57 | } 58 | // replace unnecessarily added html tags 59 | new = strings.Replace(new, "", "", 1) 60 | new = strings.Replace(new, "", "", 1) 61 | return new, nil 62 | } 63 | -------------------------------------------------------------------------------- /template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Syntax Highlighting from Markdown with Go 5 | 9 | 10 | 11 | {{.Content}} 12 | 13 | 14 | --------------------------------------------------------------------------------