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  }