Skip to content

Commit

Permalink
Add basic support for syntax highlighting in code blocks (#515)
Browse files Browse the repository at this point in the history
* Add basic support for syntax highlighting in code blocks

* Work around additional new lines from chroma

* Make both formatter and style configurable

* Consolidate configs per review

* Fixed when PrefixContext and not ShowContextMulti

* Break out codeblock formatting to separate method per review
  • Loading branch information
hloeung authored Dec 15, 2022
1 parent 3697235 commit d6583b6
Show file tree
Hide file tree
Showing 349 changed files with 59,697 additions and 20 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ require (
)

require (
github.com/alecthomas/chroma/v2 v2.4.0 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ github.com/advancedlogic/GoOse v0.0.0-20200830213114-1225d531e0ad/go.mod h1:f3HC
github.com/advancedlogic/GoOse v0.0.0-20210820140952-9d5822d4a625/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/chroma/v2 v2.4.0 h1:Loe2ZjT5x3q1bcWwemqyqEi8p11/IV/ncFCeLYDpWC4=
github.com/alecthomas/chroma/v2 v2.4.0/go.mod h1:6kHzqF5O6FUSJzBXW7fXELjb+e+7OXW4UpoPqMO7IBQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
Expand Down Expand Up @@ -451,6 +453,8 @@ github.com/dhui/dktest v0.3.7/go.mod h1:nYMOkafiA07WchSwKnKFUSbGMb2hMm5DrCGiXYG6
github.com/die-net/lrucache v0.0.0-20181227122439-19a39ef22a11/go.mod h1:ew0MSjCVDdtGMjF3kzLK9hwdgF5mOE8SbYVF3Rc7mkU=
github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
Expand Down
8 changes: 8 additions & 0 deletions matterircd.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ ShowMentions = false
# This disables that making them appear as normal PRIVMSGs.
#DisableDefaultMentions = true

# Enable syntax highlighting for code blocks.
# Formatter and Style are passed through to the chroma v2 package.
# https://github.com/alecthomas/chroma/blob/master/formatters/tty_indexed.go#L262
# terminal/terminal8 for 8-colors, terminal16, terminal256, terminal16m (16M true-colour).
# https://github.com/alecthomas/chroma/tree/master/styles
# These are different colour schemes/styles. E.g. pygments, emacs, autumn, etc.
SyntaxHighlighting = "terminal256:pygments"

# Path to file to store last viewed information. This is useful for replying only
# the messages missed.
LastViewedSaveFile = "matterircd-lastsaved.db"
Expand Down
84 changes: 64 additions & 20 deletions mm-go-irckit/userbridge.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package irckit

import (
"bytes"
"encoding/binary"
"fmt"
"math/rand"
Expand All @@ -16,6 +17,7 @@ import (
"github.com/42wim/matterircd/bridge/mastodon"
"github.com/42wim/matterircd/bridge/mattermost"
"github.com/42wim/matterircd/bridge/slack"
"github.com/alecthomas/chroma/v2/quick"
"github.com/davecgh/go-spew/spew"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/muesli/reflow/wordwrap"
Expand Down Expand Up @@ -139,22 +141,18 @@ func (u *User) handleDirectMessageEvent(event *bridge.DirectMessageEvent) {
}
text, prefix, suffix, showContext, maxlen := u.handleMessageThreadContext(prefixUser, event.MessageID, event.ParentID, event.Event, event.Text)

lexer := ""
codeBlockBackTick := false
codeBlockTilde := false
text = wordwrap.String(text, maxlen)
lines := strings.Split(text, "\n")
for _, text := range lines {
if strings.HasPrefix(text, "```") && !codeBlockTilde {
codeBlockBackTick = !codeBlockBackTick
}
if strings.HasPrefix(text, "~~~") && !codeBlockBackTick {
codeBlockTilde = !codeBlockTilde
}
// skip empty lines for anything not part of a code block.
if !codeBlockBackTick && !codeBlockTilde && text == "" {

// TODO: Ideally, we want to read the whole code block and syntax highlight on that, but let's go with per-line for now.
text, codeBlockBackTick, codeBlockTilde, lexer = u.formatCodeBlockText(text, prefix, codeBlockBackTick, codeBlockTilde, lexer)

if text == "" {
continue
} else if text == "" {
text = " "
}

if showContext {
Expand Down Expand Up @@ -289,22 +287,18 @@ func (u *User) handleChannelMessageEvent(event *bridge.ChannelMessageEvent) {
text, prefix, suffix, showContext, maxlen = u.handleMessageThreadContext(event.ChannelID, event.MessageID, event.ParentID, event.Event, event.Text)
}

lexer := ""
codeBlockBackTick := false
codeBlockTilde := false
text = wordwrap.String(text, maxlen)
lines := strings.Split(text, "\n")
for _, text := range lines {
if strings.HasPrefix(text, "```") && !codeBlockTilde {
codeBlockBackTick = !codeBlockBackTick
}
if strings.HasPrefix(text, "~~~") && !codeBlockBackTick {
codeBlockTilde = !codeBlockTilde
}
// skip empty lines for anything not part of a code block.
if !codeBlockBackTick && !codeBlockTilde && text == "" {

// TODO: Ideally, we want to read the whole code block and syntax highlight on that, but let's go with per-line for now.
text, codeBlockBackTick, codeBlockTilde, lexer = u.formatCodeBlockText(text, prefix, codeBlockBackTick, codeBlockTilde, lexer)

if text == "" {
continue
} else if text == "" {
text = " "
}

if showContext {
Expand Down Expand Up @@ -1102,3 +1096,53 @@ func (u *User) handleMessageThreadContext(channelID, messageID, parentID, event,

return newText, prefix, suffix, showContext, maxlen
}

//nolint:gocyclo
func (u *User) formatCodeBlockText(text string, prefix string, codeBlockBackTick bool, codeBlockTilde bool, lexer string) (string, bool, bool, string) {
// skip empty lines for anything not part of a code block.
if text == "" {
if codeBlockBackTick || codeBlockTilde {
return " ", codeBlockBackTick, codeBlockTilde, lexer
}
return "", codeBlockBackTick, codeBlockTilde, lexer
}

syntaxHighlighting := u.v.GetString(u.br.Protocol() + ".syntaxhighlighting")

if (strings.HasPrefix(text, "```") || strings.HasPrefix(text, prefix+"```")) && !codeBlockTilde {
codeBlockBackTick = !codeBlockBackTick
if codeBlockBackTick {
lexer = strings.TrimSpace(strings.TrimPrefix(strings.TrimPrefix(text, "```"), prefix+"```"))
}
return text, codeBlockBackTick, codeBlockTilde, lexer
}
if (strings.HasPrefix(text, "~~~") || strings.HasPrefix(text, prefix+"~~~")) && !codeBlockBackTick {
codeBlockTilde = !codeBlockTilde
if codeBlockTilde {
lexer = strings.TrimSpace(strings.TrimPrefix(strings.TrimPrefix(text, "~~~"), prefix+"~~~"))
}
return text, codeBlockBackTick, codeBlockTilde, lexer
}

if !(codeBlockBackTick || codeBlockTilde) || syntaxHighlighting == "" || lexer == "" {
return text, codeBlockBackTick, codeBlockTilde, lexer
}

formatter := "terminal256"
style := "pygments"
v := strings.SplitN(syntaxHighlighting, ":", 2)
if len(v) == 2 {
formatter = v[0]
style = v[1]
}

var b bytes.Buffer
err := quick.Highlight(&b, text, lexer, formatter, style)
if err == nil {
text = b.String()
// Work around https://github.com/alecthomas/chroma/issues/716
text = strings.ReplaceAll(text, "\n", "")
}

return text, codeBlockBackTick, codeBlockTilde, lexer
}
13 changes: 13 additions & 0 deletions vendor/github.com/alecthomas/chroma/v2/.editorconfig

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions vendor/github.com/alecthomas/chroma/v2/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 89 additions & 0 deletions vendor/github.com/alecthomas/chroma/v2/.golangci.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions vendor/github.com/alecthomas/chroma/v2/.goreleaser.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions vendor/github.com/alecthomas/chroma/v2/COPYING

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions vendor/github.com/alecthomas/chroma/v2/Makefile

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d6583b6

Please sign in to comment.