Files
git-cli/private/diff.rkt
T
2026-08-13 12:01:21 +02:00

141 lines
5.5 KiB
Racket

#lang racket/base
(require net/sendurl
racket/list
json
xml
racket/string
"config.rkt"
)
(provide diff->html
show->html)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Helper functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (make-js . args)
(string-join args "\n"))
(define (highlight-css)
(cfg-get 'diff 'highlight-css
"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github.min.css")
)
(define (diff2html-min-css)
(cfg-get 'diff 'diff2html-min-css
"https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css")
)
(define (diff2html-ui-min-js)
(cfg-get 'diff 'diff2html-ui-min-js
"https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js")
)
(define (diff-script diff)
(format
(make-js
"window.do_diff = function() {"
"const diff = ~a;"
"const ui = new Diff2HtmlUI("
" document.getElementById('diff'),"
" diff,"
" {"
" drawFileList: true,"
" matching: 'lines',"
" outputFormat: 'side-by-side',"
" });"
"ui.draw();"
"ui.highlightCode();"
"};"
)
(jsexpr->string diff))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Exported functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Render a Git diff in a temporary HTML file.
; pre : diff is a unified Git diff string.
; post : The generated HTML file has been opened in the default browser.
; result : void.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (diff->html diff)
(let ((html `(html
(head
(meta ((charset "utf-8")))
(link ((rel "stylesheet") (href ,(highlight-css) )))
(link ((rel "stylesheet") (href ,(diff2html-min-css) )))
(script ((src ,diff2html-ui-min-js)) "")
(script ,(diff-script diff))
)
(body
(div ((id "diff")))
(script ((type "text/javascript"))
"window.do_diff();")
))))
(let ((tmp-file (build-path (find-system-path 'temp-dir) "racket-git-diff.html")))
(call-with-output-file tmp-file #:exists 'truncate
(λ (out)
(display (xexpr->string html) out)))
(send-url/file tmp-file))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Render git show output as commit information followed by a diff.
; pre : show is the textual output produced by git show.
; post : The generated HTML file has been opened in the default browser.
; result : void.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (show->html show)
(let* ((lines (string-split show "\n" #:trim? #f))
(diff-pos (let loop ((rest lines) (n 0))
(cond ((null? rest) #f)
((string-prefix? (car rest) "diff --git ") n)
(else (loop (cdr rest) (+ n 1))))))
(header (string-join (if diff-pos (take lines diff-pos) lines) "\n"))
(diff (string-join (if diff-pos (drop lines diff-pos) '()) "\n"))
(highlight-css "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github.min.css")
(diff2html-min-css "https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css")
(diff2html-ui-min-js "https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js")
(html
`(html
(head
(meta ((charset "utf-8")))
(link ((rel "stylesheet") (href ,highlight-css)))
(link ((rel "stylesheet") (href ,diff2html-min-css)))
(style "body { font-family: sans-serif; margin: 1.5em; } pre.commit { white-space: pre-wrap; }")
,@(if diff-pos
`((script ((src ,diff2html-ui-min-js)) "")
(script
,(format
(make-js
"window.do_diff = function() {"
"const diff = ~a;"
"const ui = new Diff2HtmlUI("
" document.getElementById('diff'),"
" diff,"
" {"
" drawFileList: true,"
" matching: 'lines',"
" outputFormat: 'side-by-side',"
" });"
"ui.draw();"
"ui.highlightCode();"
"};")
(jsexpr->string diff))))
'()))
(body
(pre ((class "commit")) ,header)
,@(if diff-pos
`((div ((id "diff")))
(script ((type "text/javascript")) "window.do_diff();"))
'()))))
(tmp-file (build-path (find-system-path 'temp-dir) "racket-git-show.html")))
(call-with-output-file tmp-file #:exists 'truncate
(λ (out) (display (xexpr->string html) out)))
(send-url/file tmp-file)))