package main import ( "html" "html/template" "path" "strings" "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/lexers" ) var radianceLexer = chroma.MustNewLexer( &chroma.Config{Name: "Radiance"}, func() chroma.Rules { return chroma.Rules{"root": { {Pattern: `//[^\n]*`, Type: chroma.CommentSingle}, {Pattern: `"(?:\\.|[^"\\])*"|'[^']{1,2}'`, Type: chroma.LiteralString}, {Pattern: `\b(?:0x[0-9a-fA-F]+|0b[01]+|[0-9]+(?:\.[0-9]+)?)\b`, Type: chroma.LiteralNumber}, {Pattern: `@[a-zA-Z]+`, Type: chroma.NameBuiltin}, {Pattern: `\b(?:fn|if|else|for|while|break|switch|match|set|record|union|constant|align|let|use|mod|module|case|continue|return|true|false|loop|extern|panic|device|register|bit|catch|throw|throws|test|trait|instance|at|mut|nil|undefined|static|in|is|where|export|as|and|or|xor|not|try|atomic|select|of|assert)\b`, Type: chroma.Keyword}, {Pattern: `\b(?:bool|u8|u16|u32|u64|i8|i16|i32|i64|f32|void|opaque)\b`, Type: chroma.KeywordType}, {Pattern: `->|=>|::|\.\.|<>|\?{1,2}|!{1,2}|>=?|<=?|[&*.=+\-/%]`, Type: chroma.Operator}, {Pattern: `[(){}\[\]]`, Type: chroma.Punctuation}, {Pattern: `\s+`, Type: chroma.TextWhitespace}, {Pattern: `.`, Type: chroma.Text}, }} }, ) var radianceILLexer = chroma.MustNewLexer( &chroma.Config{Name: "Radiance IL"}, func() chroma.Rules { return chroma.Rules{"root": { {Pattern: `//[^\n]*`, Type: chroma.CommentSingle}, {Pattern: `"[^"\n]*"`, Type: chroma.LiteralString}, {Pattern: `%[0-9]+`, Type: chroma.NameVariable}, {Pattern: `@[A-Za-z_][A-Za-z#0-9_]*`, Type: chroma.NameLabel}, {Pattern: `\$[A-Za-z_][A-Za-z0-9_]*`, Type: chroma.Name}, {Pattern: `\b(?:-?[0-9]+|0x[a-fA-F0-9]+)\b`, Type: chroma.LiteralNumber}, {Pattern: `\b(?:fn|data|extern|mut|align)\b`, Type: chroma.Keyword}, {Pattern: `\b(?:br\.eq|br\.ne|br\.slt|br\.ult|reserve|load|sload|store|blit|copy|add|sub|mul|sdiv|udiv|srem|urem|neg|eq|ne|slt|sge|ult|uge|and|or|xor|shl|sshr|ushr|not|zext|sext|call|ret|jmp|switch|unreachable|ecall|ebreak)\b`, Type: chroma.NameBuiltin}, {Pattern: `\b(?:str|sym|undef|w8|w16|w32|w64)\b`, Type: chroma.KeywordType}, {Pattern: `[{}();:,]`, Type: chroma.Punctuation}, {Pattern: `\s+`, Type: chroma.TextWhitespace}, {Pattern: `.`, Type: chroma.Text}, }} }, ) // highlightLines returns one escaped HTML fragment for each source line. func highlightLines(filename string, lines []string) []template.HTML { if len(lines) == 0 { return nil } lexer := lexerForFilename(filename) iterator, err := lexer.Tokenise(nil, strings.Join(lines, "\n")) if err != nil { return escapeLines(lines) } highlighted := make([]strings.Builder, len(lines)) line := 0 for token := iterator(); token != chroma.EOF; token = iterator() { parts := strings.Split(token.Value, "\n") class := tokenClass(token.Type) for i, part := range parts { if i > 0 { line++ } if line >= len(highlighted) || part == "" { continue } if class != "" { highlighted[line].WriteString(``) } highlighted[line].WriteString(html.EscapeString(part)) if class != "" { highlighted[line].WriteString(``) } } } result := make([]template.HTML, len(highlighted)) for i := range highlighted { result[i] = template.HTML(highlighted[i].String()) } return result } func highlightDiff(file DiffFile, lines []DiffLine) { oldSource := make([]string, 0, len(lines)) newSource := make([]string, 0, len(lines)) for i := range lines { if lines[i].OldNum > 0 { oldSource = append(oldSource, lines[i].Content) } if lines[i].NewNum > 0 { newSource = append(newSource, lines[i].Content) } } oldHighlighted := highlightLines(file.OldName, oldSource) newHighlighted := highlightLines(file.NewName, newSource) oldIndex, newIndex := 0, 0 for i := range lines { line := &lines[i] if line.Type == "del" { line.Highlighted = oldHighlighted[oldIndex] } else { line.Highlighted = newHighlighted[newIndex] } if line.OldNum > 0 { oldIndex++ } if line.NewNum > 0 { newIndex++ } } } func lexerForFilename(filename string) chroma.Lexer { switch strings.ToLower(path.Ext(filename)) { case ".rad": return radianceLexer case ".ril": return radianceILLexer } if lexer := lexers.Match(filename); lexer != nil { return chroma.Coalesce(lexer) } return lexers.Fallback } func tokenClass(tokenType chroma.TokenType) string { switch { case tokenType.InCategory(chroma.Keyword): return "syntax-keyword" case tokenType.InSubCategory(chroma.NameBuiltin): return "syntax-builtin" case tokenType.InSubCategory(chroma.LiteralString): return "syntax-string" case tokenType.InSubCategory(chroma.LiteralNumber): return "syntax-number" case tokenType.InCategory(chroma.Comment): return "syntax-comment" case tokenType.InCategory(chroma.Operator): return "syntax-operator" case tokenType.InCategory(chroma.Punctuation): return "syntax-punctuation" case tokenType.InSubCategory(chroma.NameVariable), tokenType == chroma.NameLabel: return "syntax-name" default: return "" } } func escapeLines(lines []string) []template.HTML { escaped := make([]template.HTML, len(lines)) for i, line := range lines { escaped[i] = template.HTML(html.EscapeString(line)) } return escaped }