1
0
mirror of https://github.com/alecthomas/chroma.git synced 2025-09-16 09:16:32 +02:00

fix: use a class with line links when requested (#692)

Fixes #683
This commit is contained in:
Alec Thomas
2022-11-01 17:28:30 +11:00
committed by GitHub
parent 5e27ea4aaf
commit d0e811c0ef
8 changed files with 126 additions and 117 deletions

View File

@@ -185,7 +185,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 and be a link to themselves. - `WithLinkableLineNumbers()` - 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.

1
bin/.stringer-0.1.11.pkg Symbolic link
View File

@@ -0,0 +1 @@
hermit

1
bin/stringer Symbolic link
View File

@@ -0,0 +1 @@
.stringer-0.1.11.pkg

View File

@@ -267,7 +267,7 @@ func configureHTMLFormatter(ctx *kong.Context) {
html.WithLineNumbers(cli.HTMLLines), html.WithLineNumbers(cli.HTMLLines),
html.LineNumbersInTable(cli.HTMLLinesTable), html.LineNumbersInTable(cli.HTMLLinesTable),
html.PreventSurroundingPre(cli.HTMLPreventSurroundingPre), html.PreventSurroundingPre(cli.HTMLPreventSurroundingPre),
html.LinkableLineNumbers(cli.HTMLLinkableLines, "L"), html.WithLinkableLineNumbers(cli.HTMLLinkableLines, "L"),
} }
if len(cli.HTMLHighlight) > 0 { if len(cli.HTMLHighlight) > 0 {
ranges := [][2]int{} ranges := [][2]int{}

View File

@@ -100,9 +100,9 @@ func LineNumbersInTable(b bool) Option {
} }
} }
// LinkableLineNumbers decorates the line numbers HTML elements with an "id" // WithLinkableLineNumbers decorates the line numbers HTML elements with an "id"
// attribute so they can be linked. // attribute so they can be linked.
func LinkableLineNumbers(b bool, prefix string) Option { func WithLinkableLineNumbers(b bool, prefix string) Option {
return func(f *Formatter) { return func(f *Formatter) {
f.linkableLineNumbers = b f.linkableLineNumbers = b
f.lineNumbersIDPrefix = prefix f.lineNumbersIDPrefix = prefix
@@ -262,7 +262,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>%s\n</span>", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line)) fmt.Fprintf(w, "<span%s%s>%s\n</span>", f.styleAttr(css, chroma.LineNumbersTable), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(css, lineDigits, line))
if highlight { if highlight {
fmt.Fprintf(w, "</span>") fmt.Fprintf(w, "</span>")
@@ -302,7 +302,7 @@ func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.
// Line number // Line number
if f.lineNumbers && !wrapInTable { if f.lineNumbers && !wrapInTable {
fmt.Fprintf(w, "<span%s%s>%s</span>", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(lineDigits, line)) fmt.Fprintf(w, "<span%s%s>%s</span>", f.styleAttr(css, chroma.LineNumbers), f.lineIDAttribute(line), f.lineTitleWithLinkIfNeeded(css, lineDigits, line))
} }
fmt.Fprintf(w, `<span%s>`, f.styleAttr(css, chroma.CodeLine)) fmt.Fprintf(w, `<span%s>`, f.styleAttr(css, chroma.CodeLine))
@@ -345,12 +345,12 @@ func (f *Formatter) lineIDAttribute(line int) string {
return fmt.Sprintf(" id=\"%s\"", f.lineID(line)) return fmt.Sprintf(" id=\"%s\"", f.lineID(line))
} }
func (f *Formatter) lineTitleWithLinkIfNeeded(lineDigits, line int) string { func (f *Formatter) lineTitleWithLinkIfNeeded(css map[chroma.TokenType]string, lineDigits, line int) string {
title := fmt.Sprintf("%*d", lineDigits, line) title := fmt.Sprintf("%*d", lineDigits, line)
if !f.linkableLineNumbers { if !f.linkableLineNumbers {
return title return title
} }
return fmt.Sprintf("<a style=\"outline: none; text-decoration:none; color:inherit\" href=\"#%s\">%s</a>", f.lineID(line), title) return fmt.Sprintf("<a%s href=\"#%s\">%s</a>", f.styleAttr(css, chroma.LineLink), f.lineID(line), title)
} }
func (f *Formatter) lineID(line int) string { func (f *Formatter) lineID(line int) string {
@@ -520,6 +520,7 @@ func (f *Formatter) styleToCSS(style *chroma.Style) map[chroma.TokenType]string
classes[chroma.LineNumbersTable] = lineNumbersStyle + classes[chroma.LineNumbersTable] classes[chroma.LineNumbersTable] = lineNumbersStyle + classes[chroma.LineNumbersTable]
classes[chroma.LineTable] = "border-spacing: 0; padding: 0; margin: 0; border: 0;" + classes[chroma.LineTable] classes[chroma.LineTable] = "border-spacing: 0; padding: 0; margin: 0; border: 0;" + classes[chroma.LineTable]
classes[chroma.LineTableTD] = "vertical-align: top; padding: 0; margin: 0; border: 0;" + classes[chroma.LineTableTD] classes[chroma.LineTableTD] = "vertical-align: top; padding: 0; margin: 0; border: 0;" + classes[chroma.LineTableTD]
classes[chroma.LineLink] = "outline: none; text-decoration:none; color:inherit" + classes[chroma.LineLink]
return classes return classes
} }

View File

@@ -199,7 +199,7 @@ func TestPreWrapper(t *testing.T) {
} }
func TestLinkeableLineNumbers(t *testing.T) { func TestLinkeableLineNumbers(t *testing.T) {
f := New(WithClasses(true), WithLineNumbers(true), LinkableLineNumbers(true, "line")) f := New(WithClasses(true), WithLineNumbers(true), WithLinkableLineNumbers(true, "line"), WithClasses(false))
it, err := lexers.Get("go").Tokenise(nil, "package main\nfunc main()\n{\nprintln(\"hello world\")\n}\n") it, err := lexers.Get("go").Tokenise(nil, "package main\nfunc main()\n{\nprintln(\"hello world\")\n}\n")
assert.NoError(t, err) assert.NoError(t, err)
@@ -207,12 +207,12 @@ func TestLinkeableLineNumbers(t *testing.T) {
err = f.Format(&buf, styles.Fallback, it) err = f.Format(&buf, styles.Fallback, it)
assert.NoError(t, err) 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="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>`) 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) { func TestTableLinkeableLineNumbers(t *testing.T) {
f := New(WithClasses(true), WithLineNumbers(true), LineNumbersInTable(true), LinkableLineNumbers(true, "line")) f := New(Standalone(true), WithClasses(true), WithLineNumbers(true), LineNumbersInTable(true), WithLinkableLineNumbers(true, "line"))
it, err := lexers.Get("go").Tokenise(nil, "package main\nfunc main()\n{\nprintln(`hello world`)\n}\n") it, err := lexers.Get("go").Tokenise(nil, "package main\nfunc main()\n{\nprintln(`hello world`)\n}\n")
assert.NoError(t, err) assert.NoError(t, err)
@@ -220,8 +220,9 @@ func TestTableLinkeableLineNumbers(t *testing.T) {
err = f.Format(&buf, styles.Fallback, it) err = f.Format(&buf, styles.Fallback, it)
assert.NoError(t, err) 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="line1"><a class="lnlinks" href="#line1">1</a>`)
assert.Contains(t, buf.String(), `id="line5"><a style="outline: none; text-decoration:none; color:inherit" href="#line5">5</a>`) assert.Contains(t, buf.String(), `id="line5"><a class="lnlinks" href="#line5">5</a>`)
assert.Contains(t, buf.String(), `/* LineLinks */ .chroma .lnlinks { outline: none; text-decoration:none; color:inherit }`, buf.String())
} }
func TestTableLineNumberSpacing(t *testing.T) { func TestTableLineNumberSpacing(t *testing.T) {

View File

@@ -16,10 +16,11 @@ func _() {
_ = x[LineHighlight - -6] _ = x[LineHighlight - -6]
_ = x[LineTable - -7] _ = x[LineTable - -7]
_ = x[LineTableTD - -8] _ = x[LineTableTD - -8]
_ = x[CodeLine - -9] _ = x[LineLink - -9]
_ = x[Error - -10] _ = x[CodeLine - -10]
_ = x[Other - -11] _ = x[Error - -11]
_ = x[None - -12] _ = x[Other - -12]
_ = x[None - -13]
_ = x[EOFType-0] _ = x[EOFType-0]
_ = x[Keyword-1000] _ = x[Keyword-1000]
_ = x[KeywordConstant-1001] _ = x[KeywordConstant-1001]
@@ -108,107 +109,108 @@ func _() {
_ = x[TextPunctuation-8003] _ = x[TextPunctuation-8003]
} }
const _TokenType_name = "NoneOtherErrorCodeLineLineTableTDLineTableLineHighlightLineNumbersTableLineNumbersLinePreWrapperBackgroundEOFTypeKeywordKeywordConstantKeywordDeclarationKeywordNamespaceKeywordPseudoKeywordReservedKeywordTypeNameNameAttributeNameBuiltinNameBuiltinPseudoNameClassNameConstantNameDecoratorNameEntityNameExceptionNameFunctionNameFunctionMagicNameKeywordNameLabelNameNamespaceNameOperatorNameOtherNamePseudoNamePropertyNameTagNameVariableNameVariableAnonymousNameVariableClassNameVariableGlobalNameVariableInstanceNameVariableMagicLiteralLiteralDateLiteralOtherLiteralStringLiteralStringAffixLiteralStringAtomLiteralStringBacktickLiteralStringBooleanLiteralStringCharLiteralStringDelimiterLiteralStringDocLiteralStringDoubleLiteralStringEscapeLiteralStringHeredocLiteralStringInterpolLiteralStringNameLiteralStringOtherLiteralStringRegexLiteralStringSingleLiteralStringSymbolLiteralNumberLiteralNumberBinLiteralNumberFloatLiteralNumberHexLiteralNumberIntegerLiteralNumberIntegerLongLiteralNumberOctOperatorOperatorWordPunctuationCommentCommentHashbangCommentMultilineCommentSingleCommentSpecialCommentPreprocCommentPreprocFileGenericGenericDeletedGenericEmphGenericErrorGenericHeadingGenericInsertedGenericOutputGenericPromptGenericStrongGenericSubheadingGenericTracebackGenericUnderlineTextTextWhitespaceTextSymbolTextPunctuation" const _TokenType_name = "NoneOtherErrorCodeLineLineLinksLineTableTDLineTableLineHighlightLineNumbersTableLineNumbersLinePreWrapperBackgroundEOFTypeKeywordKeywordConstantKeywordDeclarationKeywordNamespaceKeywordPseudoKeywordReservedKeywordTypeNameNameAttributeNameBuiltinNameBuiltinPseudoNameClassNameConstantNameDecoratorNameEntityNameExceptionNameFunctionNameFunctionMagicNameKeywordNameLabelNameNamespaceNameOperatorNameOtherNamePseudoNamePropertyNameTagNameVariableNameVariableAnonymousNameVariableClassNameVariableGlobalNameVariableInstanceNameVariableMagicLiteralLiteralDateLiteralOtherLiteralStringLiteralStringAffixLiteralStringAtomLiteralStringBacktickLiteralStringBooleanLiteralStringCharLiteralStringDelimiterLiteralStringDocLiteralStringDoubleLiteralStringEscapeLiteralStringHeredocLiteralStringInterpolLiteralStringNameLiteralStringOtherLiteralStringRegexLiteralStringSingleLiteralStringSymbolLiteralNumberLiteralNumberBinLiteralNumberFloatLiteralNumberHexLiteralNumberIntegerLiteralNumberIntegerLongLiteralNumberOctOperatorOperatorWordPunctuationCommentCommentHashbangCommentMultilineCommentSingleCommentSpecialCommentPreprocCommentPreprocFileGenericGenericDeletedGenericEmphGenericErrorGenericHeadingGenericInsertedGenericOutputGenericPromptGenericStrongGenericSubheadingGenericTracebackGenericUnderlineTextTextWhitespaceTextSymbolTextPunctuation"
var _TokenType_map = map[TokenType]string{ var _TokenType_map = map[TokenType]string{
-12: _TokenType_name[0:4], -13: _TokenType_name[0:4],
-11: _TokenType_name[4:9], -12: _TokenType_name[4:9],
-10: _TokenType_name[9:14], -11: _TokenType_name[9:14],
-9: _TokenType_name[14:22], -10: _TokenType_name[14:22],
-8: _TokenType_name[22:33], -9: _TokenType_name[22:31],
-7: _TokenType_name[33:42], -8: _TokenType_name[31:42],
-6: _TokenType_name[42:55], -7: _TokenType_name[42:51],
-5: _TokenType_name[55:71], -6: _TokenType_name[51:64],
-4: _TokenType_name[71:82], -5: _TokenType_name[64:80],
-3: _TokenType_name[82:86], -4: _TokenType_name[80:91],
-2: _TokenType_name[86:96], -3: _TokenType_name[91:95],
-1: _TokenType_name[96:106], -2: _TokenType_name[95:105],
0: _TokenType_name[106:113], -1: _TokenType_name[105:115],
1000: _TokenType_name[113:120], 0: _TokenType_name[115:122],
1001: _TokenType_name[120:135], 1000: _TokenType_name[122:129],
1002: _TokenType_name[135:153], 1001: _TokenType_name[129:144],
1003: _TokenType_name[153:169], 1002: _TokenType_name[144:162],
1004: _TokenType_name[169:182], 1003: _TokenType_name[162:178],
1005: _TokenType_name[182:197], 1004: _TokenType_name[178:191],
1006: _TokenType_name[197:208], 1005: _TokenType_name[191:206],
2000: _TokenType_name[208:212], 1006: _TokenType_name[206:217],
2001: _TokenType_name[212:225], 2000: _TokenType_name[217:221],
2002: _TokenType_name[225:236], 2001: _TokenType_name[221:234],
2003: _TokenType_name[236:253], 2002: _TokenType_name[234:245],
2004: _TokenType_name[253:262], 2003: _TokenType_name[245:262],
2005: _TokenType_name[262:274], 2004: _TokenType_name[262:271],
2006: _TokenType_name[274:287], 2005: _TokenType_name[271:283],
2007: _TokenType_name[287:297], 2006: _TokenType_name[283:296],
2008: _TokenType_name[297:310], 2007: _TokenType_name[296:306],
2009: _TokenType_name[310:322], 2008: _TokenType_name[306:319],
2010: _TokenType_name[322:339], 2009: _TokenType_name[319:331],
2011: _TokenType_name[339:350], 2010: _TokenType_name[331:348],
2012: _TokenType_name[350:359], 2011: _TokenType_name[348:359],
2013: _TokenType_name[359:372], 2012: _TokenType_name[359:368],
2014: _TokenType_name[372:384], 2013: _TokenType_name[368:381],
2015: _TokenType_name[384:393], 2014: _TokenType_name[381:393],
2016: _TokenType_name[393:403], 2015: _TokenType_name[393:402],
2017: _TokenType_name[403:415], 2016: _TokenType_name[402:412],
2018: _TokenType_name[415:422], 2017: _TokenType_name[412:424],
2019: _TokenType_name[422:434], 2018: _TokenType_name[424:431],
2020: _TokenType_name[434:455], 2019: _TokenType_name[431:443],
2021: _TokenType_name[455:472], 2020: _TokenType_name[443:464],
2022: _TokenType_name[472:490], 2021: _TokenType_name[464:481],
2023: _TokenType_name[490:510], 2022: _TokenType_name[481:499],
2024: _TokenType_name[510:527], 2023: _TokenType_name[499:519],
3000: _TokenType_name[527:534], 2024: _TokenType_name[519:536],
3001: _TokenType_name[534:545], 3000: _TokenType_name[536:543],
3002: _TokenType_name[545:557], 3001: _TokenType_name[543:554],
3100: _TokenType_name[557:570], 3002: _TokenType_name[554:566],
3101: _TokenType_name[570:588], 3100: _TokenType_name[566:579],
3102: _TokenType_name[588:605], 3101: _TokenType_name[579:597],
3103: _TokenType_name[605:626], 3102: _TokenType_name[597:614],
3104: _TokenType_name[626:646], 3103: _TokenType_name[614:635],
3105: _TokenType_name[646:663], 3104: _TokenType_name[635:655],
3106: _TokenType_name[663:685], 3105: _TokenType_name[655:672],
3107: _TokenType_name[685:701], 3106: _TokenType_name[672:694],
3108: _TokenType_name[701:720], 3107: _TokenType_name[694:710],
3109: _TokenType_name[720:739], 3108: _TokenType_name[710:729],
3110: _TokenType_name[739:759], 3109: _TokenType_name[729:748],
3111: _TokenType_name[759:780], 3110: _TokenType_name[748:768],
3112: _TokenType_name[780:797], 3111: _TokenType_name[768:789],
3113: _TokenType_name[797:815], 3112: _TokenType_name[789:806],
3114: _TokenType_name[815:833], 3113: _TokenType_name[806:824],
3115: _TokenType_name[833:852], 3114: _TokenType_name[824:842],
3116: _TokenType_name[852:871], 3115: _TokenType_name[842:861],
3200: _TokenType_name[871:884], 3116: _TokenType_name[861:880],
3201: _TokenType_name[884:900], 3200: _TokenType_name[880:893],
3202: _TokenType_name[900:918], 3201: _TokenType_name[893:909],
3203: _TokenType_name[918:934], 3202: _TokenType_name[909:927],
3204: _TokenType_name[934:954], 3203: _TokenType_name[927:943],
3205: _TokenType_name[954:978], 3204: _TokenType_name[943:963],
3206: _TokenType_name[978:994], 3205: _TokenType_name[963:987],
4000: _TokenType_name[994:1002], 3206: _TokenType_name[987:1003],
4001: _TokenType_name[1002:1014], 4000: _TokenType_name[1003:1011],
5000: _TokenType_name[1014:1025], 4001: _TokenType_name[1011:1023],
6000: _TokenType_name[1025:1032], 5000: _TokenType_name[1023:1034],
6001: _TokenType_name[1032:1047], 6000: _TokenType_name[1034:1041],
6002: _TokenType_name[1047:1063], 6001: _TokenType_name[1041:1056],
6003: _TokenType_name[1063:1076], 6002: _TokenType_name[1056:1072],
6004: _TokenType_name[1076:1090], 6003: _TokenType_name[1072:1085],
6100: _TokenType_name[1090:1104], 6004: _TokenType_name[1085:1099],
6101: _TokenType_name[1104:1122], 6100: _TokenType_name[1099:1113],
7000: _TokenType_name[1122:1129], 6101: _TokenType_name[1113:1131],
7001: _TokenType_name[1129:1143], 7000: _TokenType_name[1131:1138],
7002: _TokenType_name[1143:1154], 7001: _TokenType_name[1138:1152],
7003: _TokenType_name[1154:1166], 7002: _TokenType_name[1152:1163],
7004: _TokenType_name[1166:1180], 7003: _TokenType_name[1163:1175],
7005: _TokenType_name[1180:1195], 7004: _TokenType_name[1175:1189],
7006: _TokenType_name[1195:1208], 7005: _TokenType_name[1189:1204],
7007: _TokenType_name[1208:1221], 7006: _TokenType_name[1204:1217],
7008: _TokenType_name[1221:1234], 7007: _TokenType_name[1217:1230],
7009: _TokenType_name[1234:1251], 7008: _TokenType_name[1230:1243],
7010: _TokenType_name[1251:1267], 7009: _TokenType_name[1243:1260],
7011: _TokenType_name[1267:1283], 7010: _TokenType_name[1260:1276],
8000: _TokenType_name[1283:1287], 7011: _TokenType_name[1276:1292],
8001: _TokenType_name[1287:1301], 8000: _TokenType_name[1292:1296],
8002: _TokenType_name[1301:1311], 8001: _TokenType_name[1296:1310],
8003: _TokenType_name[1311:1326], 8002: _TokenType_name[1310:1320],
8003: _TokenType_name[1320:1335],
} }
func (i TokenType) String() string { func (i TokenType) String() string {

View File

@@ -35,6 +35,8 @@ const (
LineTable LineTable
// Line numbers table TD wrapper style. // Line numbers table TD wrapper style.
LineTableTD LineTableTD
// Line number links.
LineLink
// Code line wrapper style. // Code line wrapper style.
CodeLine CodeLine
// Input that could not be tokenised. // Input that could not be tokenised.
@@ -216,6 +218,7 @@ var (
LineHighlight: "hl", LineHighlight: "hl",
LineTable: "lntable", LineTable: "lntable",
LineTableTD: "lntd", LineTableTD: "lntd",
LineLink: "lnlinks",
CodeLine: "cl", CodeLine: "cl",
Text: "", Text: "",
Whitespace: "w", Whitespace: "w",