src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/elvdoc/highlight.go (about) 1 package elvdoc 2 3 import ( 4 "regexp" 5 "strings" 6 7 "src.elv.sh/pkg/edit/highlight" 8 "src.elv.sh/pkg/ui" 9 ) 10 11 // With an empty highlight.Config, this highlighter does not check for 12 // compilation errors or non-existent commands. 13 var highlighter = highlight.NewHighlighter(highlight.Config{}) 14 15 // HighlightCodeBlock highlights a code block from Markdown. It handles thea 16 // elvish and elvish-transcript languages. It also removes comment and directive 17 // lines from elvish-transcript code blocks. 18 func HighlightCodeBlock(info, code string) ui.Text { 19 language, _, _ := strings.Cut(info, " ") 20 switch language { 21 case "elvish": 22 t, _ := highlighter.Get(code) 23 return t 24 case "elvish-transcript": 25 return highlightTranscript(code) 26 default: 27 return ui.T(code) 28 } 29 } 30 31 // Pattern for the prefix of the first line of Elvish code in a transcript. 32 var ps1Pattern = regexp.MustCompile(`^[~/][^ ]*> `) 33 34 // TODO: Ideally this should use the parser in [src.elv.sh/pkg/transcript], 35 func highlightTranscript(code string) ui.Text { 36 var tb ui.TextBuilder 37 lines := strings.Split(code, "\n") 38 for i := 0; i < len(lines); i++ { 39 line := lines[i] 40 if ps1 := ps1Pattern.FindString(line); ps1 != "" { 41 elvishLines := []string{line[len(ps1):]} 42 // Include lines that are indented with the same length of ps1. 43 ps2 := strings.Repeat(" ", len(ps1)) 44 for i++; i < len(lines) && strings.HasPrefix(lines[i], ps2); i++ { 45 elvishLines = append(elvishLines, lines[i]) 46 } 47 i-- 48 highlighted, _ := highlighter.Get(strings.Join(elvishLines, "\n")) 49 tb.WriteText(ui.T(ps1)) 50 tb.WriteText(highlighted) 51 } else if strings.HasPrefix(line, "//") { 52 // Suppress comment/directive line. 53 continue 54 } else { 55 // Write an output line. 56 tb.WriteText(ui.T(line)) 57 } 58 if i < len(lines)-1 { 59 tb.WriteText(ui.T("\n")) 60 } 61 } 62 return tb.Text() 63 }