mirror of
https://github.com/alecthomas/chroma.git
synced 2025-07-13 01:10:14 +02:00
Make linkeable lines a link to themselves
Currently its already possible to make line lumbers linkeable. Getting such a link however requires the end user to look at the pages source and then to manually edit the URL, which is not a great UX. This PR changes that to make the line numbers a link to themselves, so clicking on them gives a link to that line that can then be passed around, similiar to e.G. GitHub.
This commit is contained in:
committed by
Alec Thomas
parent
9eb358bc3f
commit
ab61726cdb
@ -183,7 +183,7 @@ following constructor options:
|
|||||||
- `ClassPrefix(prefix)` - prefix each generated CSS class.
|
- `ClassPrefix(prefix)` - prefix each generated CSS class.
|
||||||
- `TabWidth(width)` - Set the rendered tab width, in characters.
|
- `TabWidth(width)` - Set the rendered tab width, in characters.
|
||||||
- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
|
- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
|
||||||
- `LinkableLineNumbers()` - Make the line numbers linkable.
|
- `LinkableLineNumbers()` - Make the line numbers linkable and be a link to themselves.
|
||||||
- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
|
- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
|
||||||
- `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans.
|
- `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans.
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
|
|||||||
fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight))
|
fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(w, "<span%s%s>%*d\n</span>", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), lineDigits, line)
|
fmt.Fprintf(w, "<span%s%s>%s\n</span>", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line))
|
||||||
|
|
||||||
if highlight {
|
if highlight {
|
||||||
fmt.Fprintf(w, "</span>")
|
fmt.Fprintf(w, "</span>")
|
||||||
@ -237,7 +237,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if f.lineNumbers && !wrapInTable {
|
if f.lineNumbers && !wrapInTable {
|
||||||
fmt.Fprintf(w, "<span%s%s>%*d</span>", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), lineDigits, line)
|
fmt.Fprintf(w, "<span%s%s>%s</span>", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
@ -272,7 +272,19 @@ func (f *Formatter) lineIDAttribute(line int) string {
|
|||||||
if !f.linkableLineNumbers {
|
if !f.linkableLineNumbers {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(" id=\"%s%d\"", f.lineNumbersIDPrefix, line)
|
return fmt.Sprintf(" id=\"%s\"", f.lineID(line))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) lineTitleWithLinkIfNeeded(lineDigits, line int) string {
|
||||||
|
title := fmt.Sprintf("%*d", lineDigits, line)
|
||||||
|
if !f.linkableLineNumbers {
|
||||||
|
return title
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("<a style=\"outline: none; text-decoration:none; color:inherit\" href=\"#%s\">%s</a>", f.lineID(line), title)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) lineID(line int) string {
|
||||||
|
return fmt.Sprintf("%s%d", f.lineNumbersIDPrefix, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Formatter) shouldHighlight(highlightIndex, line int) (bool, bool) {
|
func (f *Formatter) shouldHighlight(highlightIndex, line int) (bool, bool) {
|
||||||
|
@ -108,6 +108,32 @@ func TestTableLineNumberNewlines(t *testing.T) {
|
|||||||
</span>`)
|
</span>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLinkeableLineNumbers(t *testing.T) {
|
||||||
|
f := New(WithClasses(true), WithLineNumbers(true), LinkableLineNumbers(true, "line"))
|
||||||
|
it, err := lexers.Get("go").Tokenise(nil, "package main\nfunc main()\n{\nprintln(\"hello world\")\n}\n")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = f.Format(&buf, styles.Fallback, it)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Contains(t, buf.String(), `id="line1"><a style="outline: none; text-decoration:none; color:inherit" href="#line1">1</a>`)
|
||||||
|
assert.Contains(t, buf.String(), `id="line5"><a style="outline: none; text-decoration:none; color:inherit" href="#line5">5</a>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTableLinkeableLineNumbers(t *testing.T) {
|
||||||
|
f := New(WithClasses(true), WithLineNumbers(true), LineNumbersInTable(true), LinkableLineNumbers(true, "line"))
|
||||||
|
it, err := lexers.Get("go").Tokenise(nil, "package main\nfunc main()\n{\nprintln(`hello world`)\n}\n")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = f.Format(&buf, styles.Fallback, it)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Contains(t, buf.String(), `id="line1"><a style="outline: none; text-decoration:none; color:inherit" href="#line1">1</a>`)
|
||||||
|
assert.Contains(t, buf.String(), `id="line5"><a style="outline: none; text-decoration:none; color:inherit" href="#line5">5</a>`)
|
||||||
|
}
|
||||||
|
|
||||||
func TestTableLineNumberSpacing(t *testing.T) {
|
func TestTableLineNumberSpacing(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
baseLineNumber int
|
baseLineNumber int
|
||||||
|
Reference in New Issue
Block a user