mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-14 19:22:09 +09:00
enhance: truncate but show long lines in diffs (#39279)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/modules/htmlutil"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/modules/translation"
|
||||
)
|
||||
@@ -27,13 +28,19 @@ func EscapeOptionsForView() EscapeOptions {
|
||||
}
|
||||
}
|
||||
|
||||
func EscapeControlHTMLTo(html template.HTML, locale translation.Locale, w htmlutil.HTMLWriter, opts ...EscapeOptions) *EscapeStatus {
|
||||
if !setting.UI.AmbiguousUnicodeDetection {
|
||||
w.WriteHTML(html)
|
||||
return &EscapeStatus{}
|
||||
}
|
||||
escaped, _ := EscapeControlReader(strings.NewReader(string(html)), w.OriginWriter(), locale, opts...)
|
||||
return escaped
|
||||
}
|
||||
|
||||
// EscapeControlHTML escapes the Unicode control sequences in a provided html document
|
||||
func EscapeControlHTML(html template.HTML, locale translation.Locale, opts ...EscapeOptions) (escaped *EscapeStatus, output template.HTML) {
|
||||
if !setting.UI.AmbiguousUnicodeDetection {
|
||||
return &EscapeStatus{}, html
|
||||
}
|
||||
sb := &strings.Builder{}
|
||||
escaped, _ = EscapeControlReader(strings.NewReader(string(html)), sb, locale, opts...) // err has been handled in EscapeControlReader
|
||||
sb, w := htmlutil.NewHTMLStringWriter()
|
||||
escaped = EscapeControlHTMLTo(html, locale, w, opts...)
|
||||
return escaped, template.HTML(sb.String())
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -140,6 +140,14 @@ func isHeader(lof string, inHunk bool) bool {
|
||||
return strings.HasPrefix(lof, cmdDiffHead) || (!inHunk && (strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++")))
|
||||
}
|
||||
|
||||
func NewGitDiffScanner(r io.Reader) *bufio.Scanner {
|
||||
// TODO: GIT-DIFF-PARSE-LONG-LINE: ideally it shouldn't use bufio.Scanner which has a limit.
|
||||
// It will cause errors if a line is very long.
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Buffer(nil, max(512*1024, int(setting.UI.MaxDisplayFileSize/16)))
|
||||
return scanner
|
||||
}
|
||||
|
||||
// CutDiffAroundLine cuts a diff of a file in way that only the given line + numberOfLine above it will be shown
|
||||
// it also recalculates hunks and adds the appropriate headers to the new diff.
|
||||
// Warning: Only one-file diffs are allowed.
|
||||
@@ -149,7 +157,7 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
|
||||
return "", nil
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(originalDiff)
|
||||
scanner := NewGitDiffScanner(originalDiff)
|
||||
hunk := make([]string, 0)
|
||||
|
||||
// begin is the start of the hunk containing searched line
|
||||
|
||||
@@ -93,7 +93,7 @@ type HTMLWriter interface {
|
||||
OriginWriter() io.Writer
|
||||
WriteString(s string) HTMLWriter
|
||||
WriteHTML(s template.HTML) HTMLWriter
|
||||
WriteFormat(fmt template.HTML, args ...any) HTMLWriter
|
||||
WriteFormatf(fmt template.HTML, args ...any) HTMLWriter
|
||||
Err() error
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func (h *htmlWriter) WriteHTML(s template.HTML) HTMLWriter {
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *htmlWriter) WriteFormat(fmt template.HTML, args ...any) HTMLWriter {
|
||||
func (h *htmlWriter) WriteFormatf(fmt template.HTML, args ...any) HTMLWriter {
|
||||
if _, err := HTMLPrintf(h.w, fmt, args...); err != nil {
|
||||
h.errs = append(h.errs, err)
|
||||
}
|
||||
@@ -135,6 +135,11 @@ func NewHTMLWriter(w io.Writer) HTMLWriter {
|
||||
return &htmlWriter{w: w}
|
||||
}
|
||||
|
||||
func NewHTMLStringWriter() (*strings.Builder, HTMLWriter) {
|
||||
sb := &strings.Builder{}
|
||||
return sb, &htmlWriter{w: sb}
|
||||
}
|
||||
|
||||
type HTMLBuilder struct {
|
||||
sb strings.Builder
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestHTMLBuilder(t *testing.T) {
|
||||
func TestHTMLWriter(t *testing.T) {
|
||||
sb := new(strings.Builder)
|
||||
w := NewHTMLWriter(sb)
|
||||
w.WriteString("<").WriteHTML("<hr>").WriteFormat("<span>%s%s</span>", ">", EscapeString(">"))
|
||||
w.WriteString("<").WriteHTML("<hr>").WriteFormatf("<span>%s%s</span>", ">", EscapeString(">"))
|
||||
assert.Equal(t, "<<hr><span>>></span>", sb.String())
|
||||
assert.NoError(t, w.Err())
|
||||
}
|
||||
|
||||
@@ -39,18 +39,18 @@ type mimeHandler struct {
|
||||
}
|
||||
|
||||
func renderCellCodeOutputTextPlain(w htmlutil.HTMLWriter, text string) error {
|
||||
w.WriteFormat(`<div class="cell-output-text"><pre>%s</pre></div>`, text)
|
||||
w.WriteFormatf(`<div class="cell-output-text"><pre>%s</pre></div>`, text)
|
||||
return w.Err()
|
||||
}
|
||||
|
||||
func renderCellCodeOutputUnsupported(w htmlutil.HTMLWriter, message string) error {
|
||||
w.WriteFormat(`<div class="cell-output-unsupported">%s</div>`, message)
|
||||
w.WriteFormatf(`<div class="cell-output-unsupported">%s</div>`, message)
|
||||
return w.Err()
|
||||
}
|
||||
|
||||
var dataMimeHandlers = sync.OnceValue(func() []mimeHandler {
|
||||
renderImage := func(w htmlutil.HTMLWriter, subtype, payload string) error {
|
||||
w.WriteFormat(`<div class="cell-output-image"><img src="data:image/%s;base64,%s"></div>`, subtype, payload)
|
||||
w.WriteFormatf(`<div class="cell-output-image"><img src="data:image/%s;base64,%s"></div>`, subtype, payload)
|
||||
return w.Err()
|
||||
}
|
||||
renderUnsupportedOutput := func(message string) func(htmlutil.HTMLWriter, string) error {
|
||||
@@ -75,11 +75,11 @@ var dataMimeHandlers = sync.OnceValue(func() []mimeHandler {
|
||||
// To future developers: don't allow custom CSS classes or attributes,
|
||||
// because ".link-action" or "data-fetch-xxx" can send POST requests and lead to XSS.
|
||||
// If you'd really like to support more, do remember to correctly sanitize the values.
|
||||
w.WriteFormat(`<div class="cell-output-html">%s</div>`, markup.Sanitize(d))
|
||||
w.WriteFormatf(`<div class="cell-output-html">%s</div>`, markup.Sanitize(d))
|
||||
return w.Err()
|
||||
}},
|
||||
{"text/latex", func(w htmlutil.HTMLWriter, d string) error {
|
||||
w.WriteFormat(`<div class="cell-output-latex"><pre><code class="language-math display">%s</code></pre></div>`, trimMathDelimiters(d))
|
||||
w.WriteFormatf(`<div class="cell-output-latex"><pre><code class="language-math display">%s</code></pre></div>`, trimMathDelimiters(d))
|
||||
return w.Err()
|
||||
}},
|
||||
{"text/plain", renderCellCodeOutputTextPlain},
|
||||
@@ -142,14 +142,14 @@ func (renderer) Render(ctx *markup.RenderContext, input io.Reader, outputWriter
|
||||
// the size is (should be) checked and/or limited by the caller to avoid OOM
|
||||
var notebook Notebook
|
||||
if err := json.NewDecoder(input).Decode(¬ebook); err != nil {
|
||||
htmlWriter.WriteFormat(`<div class="ui error message">Failed to parse notebook JSON: %v</div>`, err)
|
||||
htmlWriter.WriteFormatf(`<div class="ui error message">Failed to parse notebook JSON: %v</div>`, err)
|
||||
return htmlWriter.Err()
|
||||
}
|
||||
|
||||
// Check nbformat version
|
||||
if notebook.Nbformat < 4 {
|
||||
msg := htmlutil.HTMLFormat("This notebook uses an older format (nbformat %d). Only nbformat 4+ is supported for rendering. Please upgrade the notebook in Jupyter or view the raw JSON.", notebook.Nbformat)
|
||||
htmlWriter.WriteFormat(`<div class="file-not-rendered-prompt">%s</div>`, msg)
|
||||
htmlWriter.WriteFormatf(`<div class="file-not-rendered-prompt">%s</div>`, msg)
|
||||
return htmlWriter.Err()
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ func renderCellCode(output htmlutil.HTMLWriter, cell Cell, language string) erro
|
||||
output.WriteHTML(`<div class="cell-line">`)
|
||||
{
|
||||
if executionCount != nil {
|
||||
output.WriteFormat(`<div class="cell-left cell-prompt">In [%d]:</div>`, *executionCount)
|
||||
output.WriteFormatf(`<div class="cell-left cell-prompt">In [%d]:</div>`, *executionCount)
|
||||
} else {
|
||||
output.WriteHTML(`<div class="cell-left cell-prompt">In [ ]:</div>`)
|
||||
}
|
||||
@@ -213,7 +213,7 @@ func renderCellCode(output htmlutil.HTMLWriter, cell Cell, language string) erro
|
||||
// Highlight code
|
||||
preAttrs, codeAttrs := highlight.CodeBlockAttributes(language)
|
||||
lexer := highlight.DetectChromaLexerByFileName("", language)
|
||||
output.WriteFormat(`<div class="cell-right cell-input"><pre %s><code %s>`, preAttrs, codeAttrs)
|
||||
output.WriteFormatf(`<div class="cell-right cell-input"><pre %s><code %s>`, preAttrs, codeAttrs)
|
||||
output.WriteHTML(highlight.RenderCodeByLexer(lexer, source))
|
||||
output.WriteHTML("</code></pre></div>")
|
||||
}
|
||||
@@ -232,7 +232,7 @@ func renderCellCode(output htmlutil.HTMLWriter, cell Cell, language string) erro
|
||||
output.WriteHTML(`<div class="cell-line">`)
|
||||
{
|
||||
if hasExecutionResult && executionCount != nil {
|
||||
output.WriteFormat(`<div class="cell-left cell-prompt">Out [%d]:</div>`, *executionCount)
|
||||
output.WriteFormatf(`<div class="cell-left cell-prompt">Out [%d]:</div>`, *executionCount)
|
||||
} else {
|
||||
output.WriteHTML(`<div class="cell-left cell-prompt"></div>`)
|
||||
}
|
||||
@@ -250,7 +250,7 @@ func renderCellCode(output htmlutil.HTMLWriter, cell Cell, language string) erro
|
||||
}
|
||||
|
||||
func renderCellPrompt(output htmlutil.HTMLWriter, left, right template.HTML) {
|
||||
output.WriteFormat(`
|
||||
output.WriteFormatf(`
|
||||
<div class="notebook-cell">
|
||||
<div class="cell-line">
|
||||
<div class="cell-left cell-prompt">%s</div>
|
||||
@@ -335,7 +335,7 @@ func renderCellCodeOutput(output htmlutil.HTMLWriter, out Output) {
|
||||
// Stream output
|
||||
if out.OutputType == "stream" && out.Text != nil {
|
||||
streamName := util.Iif(out.Name == "stderr", "stderr", "stdout")
|
||||
output.WriteFormat(`<pre class="cell-output-stream stream-%s">%s</pre>`, streamName, joinSource(out.Text))
|
||||
output.WriteFormatf(`<pre class="cell-output-stream stream-%s">%s</pre>`, streamName, joinSource(out.Text))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ func renderCellCodeOutput(output htmlutil.HTMLWriter, out Output) {
|
||||
if traceback == "" && out.Ename != "" {
|
||||
traceback = fmt.Sprintf("%s: %s", out.Ename, out.Evalue)
|
||||
}
|
||||
output.WriteFormat(`<pre class="cell-output-error">%s</pre>`, traceback)
|
||||
output.WriteFormatf(`<pre class="cell-output-error">%s</pre>`, traceback)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user