mirror of
https://github.com/schollz/cowyo.git
synced 2023-08-10 21:13:00 +03:00
128 lines
2.8 KiB
Go
128 lines
2.8 KiB
Go
|
// Package highlight_go provides a syntax highlighter for Go, using go/scanner.
|
||
|
package highlight_go
|
||
|
|
||
|
import (
|
||
|
"go/scanner"
|
||
|
"go/token"
|
||
|
"io"
|
||
|
|
||
|
"github.com/sourcegraph/annotate"
|
||
|
"github.com/sourcegraph/syntaxhighlight"
|
||
|
)
|
||
|
|
||
|
// TokenKind returns a syntaxhighlight token kind value for the given tok and lit.
|
||
|
func TokenKind(tok token.Token, lit string) syntaxhighlight.Kind {
|
||
|
switch {
|
||
|
case tok.IsKeyword() || (tok.IsOperator() && tok <= token.ELLIPSIS):
|
||
|
return syntaxhighlight.Keyword
|
||
|
|
||
|
// Literals.
|
||
|
case tok == token.INT || tok == token.FLOAT || tok == token.IMAG || tok == token.CHAR:
|
||
|
return syntaxhighlight.Decimal
|
||
|
case tok == token.STRING:
|
||
|
return syntaxhighlight.String
|
||
|
case lit == "true" || lit == "false" || lit == "iota" || lit == "nil":
|
||
|
return syntaxhighlight.Literal
|
||
|
|
||
|
case tok == token.COMMENT:
|
||
|
return syntaxhighlight.Comment
|
||
|
default:
|
||
|
return syntaxhighlight.Plaintext
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Print(src []byte, w io.Writer, p syntaxhighlight.Printer) error {
|
||
|
var s scanner.Scanner
|
||
|
fset := token.NewFileSet()
|
||
|
file := fset.AddFile("", fset.Base(), len(src))
|
||
|
s.Init(file, src, nil, scanner.ScanComments)
|
||
|
|
||
|
var lastOffset int
|
||
|
|
||
|
for {
|
||
|
pos, tok, lit := s.Scan()
|
||
|
if tok == token.EOF {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
var tokString string
|
||
|
if lit != "" {
|
||
|
tokString = lit
|
||
|
} else {
|
||
|
tokString = tok.String()
|
||
|
}
|
||
|
|
||
|
// TODO: Clean this up.
|
||
|
//if tok == token.SEMICOLON {
|
||
|
if tok == token.SEMICOLON && lit == "\n" {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// Whitespace between previous and current tokens.
|
||
|
offset := fset.Position(pos).Offset
|
||
|
if whitespace := string(src[lastOffset:offset]); whitespace != "" {
|
||
|
err := p.Print(w, syntaxhighlight.Whitespace, whitespace)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
lastOffset = offset + len(tokString)
|
||
|
|
||
|
err := p.Print(w, TokenKind(tok, lit), tokString)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Print final whitespace after the last token.
|
||
|
if whitespace := string(src[lastOffset:]); whitespace != "" {
|
||
|
err := p.Print(w, syntaxhighlight.Whitespace, whitespace)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func Annotate(src []byte, a syntaxhighlight.Annotator) (annotate.Annotations, error) {
|
||
|
var s scanner.Scanner
|
||
|
fset := token.NewFileSet()
|
||
|
file := fset.AddFile("", fset.Base(), len(src))
|
||
|
s.Init(file, src, nil, scanner.ScanComments)
|
||
|
|
||
|
var anns annotate.Annotations
|
||
|
|
||
|
for {
|
||
|
pos, tok, lit := s.Scan()
|
||
|
if tok == token.EOF {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
offset := fset.Position(pos).Offset
|
||
|
|
||
|
var tokString string
|
||
|
if lit != "" {
|
||
|
tokString = lit
|
||
|
} else {
|
||
|
tokString = tok.String()
|
||
|
}
|
||
|
|
||
|
// TODO: Clean this up.
|
||
|
//if tok == token.SEMICOLON {
|
||
|
if tok == token.SEMICOLON && lit == "\n" {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
ann, err := a.Annotate(offset, TokenKind(tok, lit), tokString)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if ann != nil {
|
||
|
anns = append(anns, ann)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return anns, nil
|
||
|
}
|