diff --git a/emoji.go b/emoji.go new file mode 100644 index 0000000..e700c86 --- /dev/null +++ b/emoji.go @@ -0,0 +1,76 @@ +// This is taken from Hugo +// +// Copyright 2016 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "sync" + + "github.com/kyokomi/emoji" +) + +var ( + emojiInit sync.Once + + emojis = make(map[string][]byte) + + emojiDelim = []byte(":") + emojiWordDelim = []byte(" ") + emojiMaxSize int +) + +func emojify(source []byte) []byte { + emojiInit.Do(initEmoji) + start := 0 + k := bytes.Index(source[start:], emojiDelim) + for k != -1 { + j := start + k + upper := j + emojiMaxSize + if upper > len(source) { + upper = len(source) + } + endEmoji := bytes.Index(source[j+1:upper], emojiDelim) + nextWordDelim := bytes.Index(source[j:upper], emojiWordDelim) + if endEmoji < 0 { + start++ + } else if endEmoji == 0 || (nextWordDelim != -1 && nextWordDelim < endEmoji) { + start += endEmoji + 1 + } else { + endKey := endEmoji + j + 2 + emojiKey := source[j:endKey] + if e, ok := emojis[string(emojiKey)]; ok { + source = append(source[:j], append(e, source[endKey:]...)...) + } + start += endEmoji + } + if start >= len(source) { + break + } + k = bytes.Index(source[start:], emojiDelim) + } + return source +} + +func initEmoji() { + emojiMap := emoji.CodeMap() + for k, v := range emojiMap { + emojis[k] = []byte(v) + if len(k) > emojiMaxSize { + emojiMaxSize = len(k) + } + } + +} diff --git a/go.mod b/go.mod index f6f5c59..a70fb87 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/kr/text v0.2.0 // indirect + github.com/kyokomi/emoji v2.2.4+incompatible github.com/labstack/echo/v4 v4.1.16 github.com/lib/pq v1.8.0 // indirect github.com/lopezator/migrator v0.3.0 @@ -23,6 +24,7 @@ require ( github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.6.1 // indirect github.com/valyala/fasttemplate v1.2.0 // indirect + github.com/yuin/goldmark v1.2.0 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 // indirect diff --git a/go.sum b/go.sum index 3b2cd32..160a87c 100644 --- a/go.sum +++ b/go.sum @@ -122,6 +122,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kyokomi/emoji v2.2.4+incompatible h1:np0woGKwx9LiHAQmwZx79Oc0rHpNw3o+3evou4BEPv4= +github.com/kyokomi/emoji v2.2.4+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA= github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o= github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= @@ -235,6 +237,8 @@ github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.2.0 h1:y3yXRCoDvC2HTtIHvL2cc7Zd+bqA+zqDO6oQzsJO07E= github.com/valyala/fasttemplate v1.2.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.2.0 h1:WOOcyaJPlzb8fZ8TloxFe8QZkhOOJx87leDa9MIT9dc= +github.com/yuin/goldmark v1.2.0/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= diff --git a/markdown.go b/markdown.go new file mode 100644 index 0000000..02900a5 --- /dev/null +++ b/markdown.go @@ -0,0 +1,36 @@ +package main + +import ( + "bytes" + _ "bytes" + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/extension" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/renderer/html" +) + +var markdown goldmark.Markdown + +func init() { + markdown = goldmark.New( + goldmark.WithRendererOptions( + html.WithUnsafe(), + ), + goldmark.WithParserOptions( + parser.WithAutoHeadingID(), + ), + goldmark.WithExtensions( + extension.GFM, + extension.Footnote, + extension.Typographer, + ), + ) +} + +func renderMarkdown(source string) (content string, err error) { + context := parser.NewContext() + var buffer bytes.Buffer + err = markdown.Convert([]byte(source), &buffer, parser.WithContext(context)) + content = string(emojify(buffer.Bytes())) + return +} diff --git a/posts.go b/posts.go index c44ce9e..fc69a8a 100644 --- a/posts.go +++ b/posts.go @@ -24,7 +24,11 @@ func servePost(c echo.Context) error { } else if err != nil { return err } - return c.String(http.StatusOK, post.content) + htmlContent, err := renderMarkdown(post.content) + if err != nil { + return err + } + return c.HTML(http.StatusOK, htmlContent) } func getPost(path string) (*post, error) {