better progress information for git commands

This commit is contained in:
2026-08-11 09:36:14 +02:00
parent a72604e290
commit daa1891967
15 changed files with 2994 additions and 78 deletions
+21
View File
@@ -0,0 +1,21 @@
#lang racket
(require racket-makefile
package-zipper
)
(target all
(displayln "use (make clean) or (make package)")
)
(target clean
(for-each (λ (f) (displayln f) (rm-f f)) (list-files "." #px"([.]bak|~)$" #:recursive #t))
(for-each (λ (d) (displayln d) (rm-rf d)) (list-dirs "." #px"(compiled|doc)$" #:recursive #t))
(for-each (λ (f) (displayln f) (rm-f f)) (list-files "scribblings" #px"[.](css|js|html)$"))
)
(target package
(deps clean)
(zip-package))
+4 -1
View File
@@ -79,13 +79,16 @@ operations use them automatically:
(git 'fetch) (git 'fetch)
(git 'pull) (git 'pull)
(git 'push) (git 'push)
;; Suppress network progress when desired:
(git 'push #:quiet #t)
``` ```
## Supported Git operations ## Supported Git operations
Version 0.2 supports repository discovery, init, clone, status, add, config, Version 0.2 supports repository discovery, init, clone, status, add, config,
commit, branch, checkout, lightweight tags, log, remotes, fetch, commit, branch, checkout, lightweight tags, log, remotes, fetch,
fast-forward-only pull, push, tag push, and HTTPS username/token credentials. fast-forward-only pull, push, tag push, network transfer progress, and HTTPS username/token credentials.
SSH credentials, merge/rebase pull, annotated tags, and submodules are not yet SSH credentials, merge/rebase pull, annotated tags, and submodules are not yet
part of this module. part of this module.
+1 -1
View File
@@ -2,7 +2,7 @@
(define collection "git") (define collection "git")
(define pkg-desc "Command-line-like Git operations for Racket, implemented with libgit2") (define pkg-desc "Command-line-like Git operations for Racket, implemented with libgit2")
(define version "0.2.5") (define version "0.2.6")
(define pkg-authors '("Hans Dijkema")) (define pkg-authors '("Hans Dijkema"))
(define license 'MIT) (define license 'MIT)
+128 -50
View File
@@ -64,6 +64,7 @@
(define GIT-CREDTYPE-USERPASS-PLAINTEXT #x0001) (define GIT-CREDTYPE-USERPASS-PLAINTEXT #x0001)
(define GIT-CREDTYPE-USERNAME #x0020) (define GIT-CREDTYPE-USERNAME #x0020)
(define GIT-PASSTHROUGH -30) (define GIT-PASSTHROUGH -30)
(define GIT-CHECKOUT-OPTIONS-VERSION 1)
(define (git-credential-callback out url username-from-url allowed-types _payload) (define (git-credential-callback out url username-from-url allowed-types _payload)
;; Never let a Racket exception escape through a C callback. In particular, ;; Never let a Racket exception escape through a C callback. In particular,
@@ -96,26 +97,88 @@
(set-git_remote_callbacks-credentials! callbacks git-credential-callback) (set-git_remote_callbacks-credentials! callbacks git-credential-callback)
callbacks) callbacks)
(define (make-fetch-options) (define (progress-message quiet format-string . args)
(unless quiet
(apply fprintf (current-output-port) format-string args)
(newline)
(flush-output)))
(define (make-progress-reporter label quiet #:bytes? [bytes? #t])
(define last-percent -10)
(lambda (current total bytes)
(unless (or quiet (zero? total))
(define percent
(min 100 (quotient (* current 100) total)))
(when (and (positive? current)
(or (>= (- percent last-percent) 10)
(= current total)))
(set! last-percent percent)
(if bytes?
(progress-message
#f
"[git] ~a: ~a% (~a/~a objects, ~a KiB)"
label percent current total (quotient (+ bytes 1023) 1024))
(progress-message
#f
"[git] ~a: ~a% (~a/~a objects)"
label percent current total))))))
(define (transfer-progress-total-objects stats)
(ptr-ref stats _uint 0))
(define (transfer-progress-received-objects stats)
(ptr-ref stats _uint 2))
(define (transfer-progress-received-bytes stats)
;; git_transfer_progress has six unsigned-int fields followed by size_t.
(ptr-ref (ptr-add stats (* 6 (ctype-sizeof _uint))) _size))
(define (set-fetch-progress-callback! callbacks label quiet)
(define report (make-progress-reporter label quiet))
(set-git_remote_callbacks-transfer_progress!
callbacks
(lambda (stats _payload)
(report (transfer-progress-received-objects stats)
(transfer-progress-total-objects stats)
(transfer-progress-received-bytes stats))
0))
callbacks)
(define (set-push-progress-callback! callbacks label quiet)
(define report (make-progress-reporter label quiet #:bytes? #f))
(set-git_remote_callbacks-push_transfer_progress!
callbacks
(lambda (current total bytes _payload)
(report current total bytes)
0))
callbacks)
(define (make-fetch-options label quiet)
(define options (define options
(cast (malloc _git_fetch_opts 'atomic) _pointer _git_fetch_opts-pointer)) (cast (malloc _git_fetch_opts 'atomic) _pointer _git_fetch_opts-pointer))
(git_fetch_options_init options GIT_FETCH_OPTS_VERSION) (git_fetch_options_init options GIT_FETCH_OPTS_VERSION)
(set-credential-callback! (git_fetch_opts-callbacks options)) (define callbacks (git_fetch_opts-callbacks options))
(set-credential-callback! callbacks)
(set-fetch-progress-callback! callbacks label quiet)
options) options)
(define (make-push-options) (define (make-push-options label quiet)
(define options (define options
(cast (malloc _git_push_opts 'atomic) _pointer _git_push_opts-pointer)) (cast (malloc _git_push_opts 'atomic) _pointer _git_push_opts-pointer))
(git_push_options_init options GIT_PUSH_OPTS_VERSION) (git_push_options_init options GIT_PUSH_OPTS_VERSION)
(set-credential-callback! (git_push_opts-callbacks options)) (define callbacks (git_push_opts-callbacks options))
(set-credential-callback! callbacks)
(set-push-progress-callback! callbacks label quiet)
options) options)
(define (make-clone-options) (define (make-clone-options label quiet)
(define options (define options
(cast (malloc _git_clone_opts 'atomic) _pointer _git_clone_opts-pointer)) (cast (malloc _git_clone_opts 'atomic) _pointer _git_clone_opts-pointer))
(git_clone_options_init options GIT_CLONE_OPTS_VERSION) (git_clone_options_init options GIT_CLONE_OPTS_VERSION)
(set-credential-callback! (define callbacks
(git_fetch_opts-callbacks (git_clone_opts-fetch_opts options))) (git_fetch_opts-callbacks (git_clone_opts-fetch_opts options)))
(set-credential-callback! callbacks)
(set-fetch-progress-callback! callbacks label quiet)
options) options)
(define (blank-oid) (define (blank-oid)
@@ -152,15 +215,14 @@
(error 'git-clone "cannot derive a directory name from ~a" url)) (error 'git-clone "cannot derive a directory name from ~a" url))
(regexp-replace #rx"[.]git$" (last parts) "")) (regexp-replace #rx"[.]git$" (last parts) ""))
(define git-clone (define (git-clone url [path (default-clone-directory url)] #:quiet [quiet #f])
(case-lambda (define label (format "clone ~a" url))
[(url) (progress-message quiet "[git] ~a" label)
(git-clone url (default-clone-directory url))]
[(url path)
(git_clone url (git_clone url
(path->string (if (path? path) path (string->path path))) (path->string (if (path? path) path (string->path path)))
(make-clone-options)) (make-clone-options label quiet))
(git-root path)])) (progress-message quiet "[git] ~a: done" label)
(git-root path))
(define (normalize-status-flags flags) (define (normalize-status-flags flags)
(cond (cond
@@ -397,19 +459,29 @@
(git_branch_delete ref) (git_branch_delete ref)
(void)) (void))
(define (make-safe-checkout-options)
;; A null checkout-options pointer means GIT_CHECKOUT_NONE (dry run).
;; Initialize the options explicitly so checkout actually updates the index
;; and worktree while preserving local modifications.
(define options
(cast (malloc _git_checkout_opts 'atomic) _pointer _git_checkout_opts-pointer))
(git_checkout_options_init options GIT-CHECKOUT-OPTIONS-VERSION)
options)
(define (git-checkout name) (define (git-checkout name)
(define repo (open-repository)) (define repo (open-repository))
(define options (make-safe-checkout-options))
(cond (cond
[(member name (git-branches)) [(member name (git-branches))
(define refname (string-append branch-prefix name)) (define refname (string-append branch-prefix name))
(define object (git_revparse_single repo (string-append refname "^{commit}"))) (define object (git_revparse_single repo (string-append refname "^{commit}")))
;; Checkout first: with default safe checkout, a dirty worktree aborts ;; Checkout first: with default safe checkout, a dirty worktree aborts
;; before HEAD is changed. ;; before HEAD is changed.
(git_checkout_tree repo object #f) (git_checkout_tree repo object options)
(git_repository_set_head repo refname)] (git_repository_set_head repo refname)]
[else [else
(define object (git_revparse_single repo (string-append name "^{commit}"))) (define object (git_revparse_single repo (string-append name "^{commit}")))
(git_checkout_tree repo object #f) (git_checkout_tree repo object options)
(git_repository_set_head_detached repo (git_object_id object))]) (git_repository_set_head_detached repo (git_object_id object))])
(git-head)) (git-head))
@@ -518,19 +590,26 @@
remote-name)) remote-name))
url) url)
(define (git-fetch [name "origin"]) (define (fetch-remote name quiet label)
(check-remote-credentials 'git-fetch name) (check-remote-credentials 'git-fetch name)
(progress-message quiet "[git] ~a" label)
(define repo (open-repository)) (define repo (open-repository))
(define remote (git_remote_lookup repo name)) (define remote (git_remote_lookup repo name))
(git_remote_fetch remote #f (make-fetch-options) (format "fetch ~a" name)) (git_remote_fetch remote #f (make-fetch-options label quiet) label)
(progress-message quiet "[git] ~a: done" label)
(void)) (void))
(define (git-pull [remote-name "origin"]) (define (git-fetch [name "origin"] #:quiet [quiet #f])
(fetch-remote name quiet (format "fetch ~a" name)))
(define (git-pull [remote-name "origin"] #:quiet [quiet #f])
(define branch (git-current-branch)) (define branch (git-current-branch))
(unless branch (unless branch
(error 'git-pull "pull requires an attached local branch")) (error 'git-pull "pull requires an attached local branch"))
(git-fetch remote-name) (define label (format "pull ~a/~a" remote-name branch))
(progress-message quiet "[git] ~a" label)
(fetch-remote remote-name quiet (format "fetch ~a" remote-name))
(define repo (open-repository)) (define repo (open-repository))
(define local-ref-name (string-append branch-prefix branch)) (define local-ref-name (string-append branch-prefix branch))
@@ -542,13 +621,17 @@
(define remote-id (git_object_id remote-object)) (define remote-id (git_object_id remote-object))
(cond (cond
[(git_oid_equal local-id remote-id) #f] [(git_oid_equal local-id remote-id)
(progress-message quiet "[git] ~a: already up to date" label)
#f]
[(git_graph_descendant_of repo remote-id local-id) [(git_graph_descendant_of repo remote-id local-id)
;; Update the worktree safely before moving the branch reference. ;; Update the worktree safely before moving the branch reference.
(git_checkout_tree repo remote-object #f) (git_checkout_tree repo remote-object (make-safe-checkout-options))
(git_reference_set_target (git_reference_set_target
local-ref remote-id (format "pull: fast-forward from ~a" remote-name)) local-ref remote-id (format "pull: fast-forward from ~a" remote-name))
(git_oid_fmt remote-id)] (define oid (git_oid_fmt remote-id))
(progress-message quiet "[git] ~a: fast-forwarded" label)
oid]
[else [else
(error 'git-pull (error 'git-pull
"non-fast-forward pull is not supported; merge or rebase explicitly")])) "non-fast-forward pull is not supported; merge or rebase explicitly")]))
@@ -558,15 +641,16 @@
(inexact->exact (floor (current-inexact-milliseconds))) (inexact->exact (floor (current-inexact-milliseconds)))
(random 1000000000))) (random 1000000000)))
(define (push-refspec remote-name refspec) (define (push-refspec remote-name refspec quiet label)
(define url (check-remote-credentials 'git-push remote-name)) (define url (check-remote-credentials 'git-push remote-name))
(when (and (http-remote? url) (when (and (http-remote? url)
(not (git-credentials-configured? url))) (not (git-credentials-configured? url)))
(error 'git-push (error 'git-push
"no HTTPS credentials are stored for ~a; use (git 'credentials 'set <url> <username> <token>)" "no HTTPS credentials are stored for ~a; use (git 'credentials 'set <url> <username> <token>)"
url)) url))
(progress-message quiet "[git] ~a" label)
(define repo (open-repository)) (define repo (open-repository))
(define options (make-push-options)) (define options (make-push-options label quiet))
;; The public Racket binding for git_remote_push cannot currently marshal a ;; The public Racket binding for git_remote_push cannot currently marshal a
;; non-null git_strarray correctly. Its null form is public and supported: ;; non-null git_strarray correctly. Its null form is public and supported:
;; libgit2 then uses the remote's configured push refspecs. Use a temporary ;; libgit2 then uses the remote's configured push refspecs. Use a temporary
@@ -589,39 +673,32 @@
(git_config_delete_entry (git_config_delete_entry
config config
(format "remote.~a.~a" temp-name suffix)))))) (format "remote.~a.~a" temp-name suffix))))))
(progress-message quiet "[git] ~a: done" label)
(void)) (void))
(define git-push (define (git-push [remote-name "origin"] [branch #f] #:quiet [quiet #f])
(case-lambda (define actual-branch (or branch (git-current-branch)))
[() (unless actual-branch
(define branch (git-current-branch))
(unless branch
(error 'git-push "push requires an attached local branch")) (error 'git-push "push requires an attached local branch"))
(git-push "origin" branch)]
[(remote-name)
(define branch (git-current-branch))
(unless branch
(error 'git-push "push requires an attached local branch"))
(git-push remote-name branch)]
[(remote-name branch)
(push-refspec (push-refspec
remote-name remote-name
(format "refs/heads/~a:refs/heads/~a" branch branch))])) (format "refs/heads/~a:refs/heads/~a" actual-branch actual-branch)
quiet
(format "push ~a/~a" remote-name actual-branch)))
(define git-push-tag (define (git-push-tag tag [remote-name "origin"] #:quiet [quiet #f])
(case-lambda
[(tag) (git-push-tag tag "origin")]
[(tag remote-name)
(push-refspec (push-refspec
remote-name remote-name
(format "refs/tags/~a:refs/tags/~a" tag tag))])) (format "refs/tags/~a:refs/tags/~a" tag tag)
quiet
(format "push tag ~a to ~a" tag remote-name)))
(define (git command . args) (define (git command #:quiet [quiet #f] . args)
(unless (symbol? command) (unless (symbol? command)
(raise-argument-error 'git "symbol?" command)) (raise-argument-error 'git "symbol?" command))
(case command (case command
[(init) (apply git-init args)] [(init) (apply git-init args)]
[(clone) (apply git-clone args)] [(clone) (keyword-apply git-clone '(#:quiet) (list quiet) args)]
[(status) (apply git-status args)] [(status) (apply git-status args)]
[(diff) (apply git-diff args)] [(diff) (apply git-diff args)]
[(add) (apply git-add args)] [(add) (apply git-add args)]
@@ -646,10 +723,10 @@
[(list 'add name url) (git-remote-add name url)] [(list 'add name url) (git-remote-add name url)]
[(list 'get-url name) (git-remote-url name)] [(list 'get-url name) (git-remote-url name)]
[_ (error 'git "invalid remote arguments: ~e" args)])] [_ (error 'git "invalid remote arguments: ~e" args)])]
[(fetch) (apply git-fetch args)] [(fetch) (keyword-apply git-fetch '(#:quiet) (list quiet) args)]
[(pull) (apply git-pull args)] [(pull) (keyword-apply git-pull '(#:quiet) (list quiet) args)]
[(push) (apply git-push args)] [(push) (keyword-apply git-push '(#:quiet) (list quiet) args)]
[(push-tag) (apply git-push-tag args)] [(push-tag) (keyword-apply git-push-tag '(#:quiet) (list quiet) args)]
[(credentials) [(credentials)
(match args (match args
[(list 'init password) (git-credentials-init! password)] [(list 'init password) (git-credentials-init! password)]
@@ -706,7 +783,8 @@
[(void? result) (void)] [(void? result) (void)]
[else (displayln result out)])) [else (displayln result out)]))
(define (dgit command . args) (define (dgit command #:quiet [quiet #f] . args)
(define result (apply git command args)) (define result
(keyword-apply git '(#:quiet) (list quiet) (cons command args)))
(display-git-result command result) (display-git-result command result)
result) result)
File diff suppressed because one or more lines are too long
+12 -13
View File
@@ -23,11 +23,11 @@ The short form is intended for build scripts and interactive use:
(git 'checkout "main") (git 'checkout "main")
] ]
@defproc[(git [command symbol?] [argument any/c] ...) any/c]{ @defproc[(git [command symbol?] [#:quiet quiet any/c #f] [argument any/c] ...) any/c]{
Dispatches @racket[command] to the corresponding Git procedure. For example, @racket[(git 'status)] calls @racket[git-status], and @racket[(git 'commit "message")] calls @racket[git-commit]. Command names are ordinary symbols, so @racket[git] can safely be used inside other macros and DSLs. Dispatches @racket[command] to the corresponding Git procedure. For example, @racket[(git 'status)] calls @racket[git-status], and @racket[(git 'commit "message")] calls @racket[git-commit]. Command names are ordinary symbols, so @racket[git] can safely be used inside other macros and DSLs. For network commands, @racket[#:quiet] suppresses progress output.
} }
@defproc[(dgit [command symbol?] [argument any/c] ...) any/c]{ @defproc[(dgit [command symbol?] [#:quiet quiet any/c #f] [argument any/c] ...) any/c]{
Calls @racket[git], displays its result in a compact human-readable form, and returns the original result. Status entries are displayed with labels such as @tt{Modified}, @tt{New}, @tt{Deleted}, and @tt{Renamed}. Ignored files remain omitted, just as with @racket[git-status]. Calls @racket[git], displays its result in a compact human-readable form, and returns the original result. Status entries are displayed with labels such as @tt{Modified}, @tt{New}, @tt{Deleted}, and @tt{Renamed}. Ignored files remain omitted, just as with @racket[git-status].
} }
@@ -43,8 +43,8 @@ Calls @racket[git], displays its result in a compact human-readable form, and re
@defproc[(git-init [path path-string? (current-directory)] [#:bare? bare? any/c #f]) path?]{Initializes a repository.} @defproc[(git-init [path path-string? (current-directory)] [#:bare? bare? any/c #f]) path?]{Initializes a repository.}
@defproc*[([(git-clone [url string?]) path?] @defproc*[([(git-clone [url string?] [#:quiet quiet any/c #f]) path?]
[(git-clone [url string?] [path path-string?]) path?])]{Clones @racket[url]. If @racket[path] is omitted, a directory name is derived from the URL.} [(git-clone [url string?] [path path-string?] [#:quiet quiet any/c #f]) path?])]{Clones @racket[url]. If @racket[path] is omitted, a directory name is derived from the URL. Progress is written to the current output port unless @racket[quiet] is true.}
@section{Status and index} @section{Status and index}
@@ -56,8 +56,8 @@ Calls @racket[git], displays its result in a compact human-readable form, and re
@defproc[(git-clean?) boolean?]{Returns @racket[#t] when @racket[git-status] is empty.} @defproc[(git-clean?) boolean?]{Returns @racket[#t] when @racket[git-status] is empty.}
@defproc*([[(git-diff) string?] @defproc*[([(git-diff) string?]
[(git-diff [option (or/c '--cached)]) string?]])]{Returns a unified patch as a string. With no arguments it compares the index with the worktree, like @tt{git diff}. With @racket['--cached] it compares HEAD with the index, like @tt{git diff --cached}.} [(git-diff [option (or/c '--cached)]) string?])]{Returns a unified patch as a string. With no arguments it compares the index with the worktree, like @tt{git diff}. With @racket['--cached] it compares HEAD with the index, like @tt{git diff --cached}.}
@defproc[(git-add [path path-string?] ...) void?]{Stages the given paths. With no paths, stages the whole repository, including tracked removals.} @defproc[(git-add [path path-string?] ...) void?]{Stages the given paths. With no paths, stages the whole repository, including tracked removals.}
@@ -105,15 +105,13 @@ Calls @racket[git], displays its result in a compact human-readable form, and re
@defproc[(git-remote-url [name string? "origin"]) string?]{Returns the remote URL.} @defproc[(git-remote-url [name string? "origin"]) string?]{Returns the remote URL.}
@defproc[(git-fetch [remote string? "origin"]) void?]{Fetches the configured refspecs from a remote.} @defproc[(git-fetch [remote string? "origin"] [#:quiet quiet any/c #f]) void?]{Fetches the configured refspecs from a remote. Progress is written to the current output port unless @racket[quiet] is true.}
@defproc[(git-pull [remote string? "origin"]) (or/c string? #f)]{Fetches and performs a fast-forward-only update of the current branch. Returns the new OID, or @racket[#f] when already up to date. A non-fast-forward update raises an exception.} @defproc[(git-pull [remote string? "origin"] [#:quiet quiet any/c #f]) (or/c string? #f)]{Fetches and performs a fast-forward-only update of the current branch. Returns the new OID, or @racket[#f] when already up to date. A non-fast-forward update raises an exception.}
@defproc*[([(git-push) void?] @defproc[(git-push [remote string? "origin"] [branch (or/c string? #f) #f] [#:quiet quiet any/c #f]) void?]{Pushes a branch to a branch with the same name. With no positional arguments, the current branch is pushed to @tt{origin}; with only @racket[remote], the current branch is pushed there. Progress is written to the current output port unless @racket[quiet] is true.}
[(git-push [remote string?]) void?]
[(git-push [remote string?] [branch string?]) void?])]{Pushes a branch to a branch with the same name. With no arguments, the current branch is pushed to @tt{origin}; with only @racket[remote], the current branch is pushed there.}
@defproc[(git-push-tag [tag string?] [remote string? "origin"]) void?]{Pushes one tag.} @defproc[(git-push-tag [tag string?] [remote string? "origin"] [#:quiet quiet any/c #f]) void?]{Pushes one tag. Progress is written to the current output port unless @racket[quiet] is true.}
Remote HTTPS operations automatically use credentials from the @tt{racket-git} credential store when an entry exists for the remote host. Remote HTTPS operations automatically use credentials from the @tt{racket-git} credential store when an entry exists for the remote host.
@@ -143,6 +141,7 @@ The following command-like forms are supported directly:
(git 'fetch) (git 'fetch)
(git 'pull) (git 'pull)
(git 'push) (git 'push)
(git 'push #:quiet #t)
(git 'push-tag "v0.1") (git 'push-tag "v0.1")
] ]
File diff suppressed because one or more lines are too long
+326
View File
@@ -0,0 +1,326 @@
/* See the beginning of "manual.css". */
/* Monospace: */
.RktIn, .RktRdr, .RktPn, .RktMeta,
.RktMod, .RktKw, .RktVar, .RktSym,
.RktRes, .RktOut, .RktCmt, .RktVal,
.RktBlk, .RktErr {
font-family: 'Fira-Mono', monospace;
white-space: inherit;
font-size: 1rem;
line-height: 1.5;
}
/* this selctor grabs the first linked Racket symbol
in a definition box (i.e., the symbol being defined) */
a.RktValDef, a.RktStxDef, a.RktSymDef,
span.RktValDef, span.RktStxDef, span.RktSymDef
{
font-size: 1.1rem;
color: black;
font-weight: 500;
}
.inheritedlbl {
font-family: 'Fira', sans-serif;
}
.RBackgroundLabelInner {
font-family: inherit;
}
/* ---------------------------------------- */
/* Inherited methods, left margin */
.inherited {
width: 95%;
margin-top: 0.5em;
text-align: left;
background-color: inherit;
}
.inherited td {
font-size: 82%;
padding-left: 0.5rem;
line-height: 1.3;
text-indent: 0;
padding-right: 0;
}
.inheritedlbl {
font-style: normal;
}
/* ---------------------------------------- */
/* Racket text styles */
.RktIn {
color: #cc6633;
background-color: #eee;
white-space: pre;
}
.RktInBG {
background-color: #eee;
}
.refcolumn .RktInBG {
background-color: white;
}
.RktRdr {
}
.RktPn {
color: #843c24;
}
.RktMeta {
color: black;
}
.RktMod {
color: inherit;
}
.RktOpt {
color: black;
font-style: italic;
}
.RktKw {
color: black;
}
.RktErr {
color: red;
font-style: italic;
font-weight: 400;
}
.RktVar {
position: relative;
left: -1px; font-style: italic;
color: #444;
}
.SVInsetFlow .RktVar {
font-weight: 400;
color: #444;
}
.RktSym {
color: inherit;
}
.RktValLink, .RktStxLink, .RktModLink {
text-decoration: none;
color: #07A;
font-size: 1rem;
}
/* for syntax links within headings */
h1 a.RktStxLink, h2 a.RktStxLink, h3 a.RktStxLink, h4 a.RktStxLink, h5 a.RktStxLink,
h1 a.RktValLink, h2 a.RktValLink, h3 a.RktValLink, h4 a.RktValLink, h5 a.RktValLink,
h1 .RktSym, h2 .RktSym, h3 .RktSym, h4 .RktSym, h5 .RktSym,
h1 .RktMod, h2 .RktMod, h3 .RktMod, h4 .RktMod, h5 .RktMod,
h1 .RktVal, h2 .RktVal, h3 .RktVal, h4 .RktVal, h5 .RktVal,
h1 .RktPn, h2 .RktPn, h3 .RktPn, h4 .RktPn, h5 .RktPn {
color: #333;
font-size: 1.50rem;
font-weight: 400;
}
.toptoclink .RktStxLink, .toclink .RktStxLink,
.toptoclink .RktValLink, .toclink .RktValLink,
.toptoclink .RktModLink, .toclink .RktModLink {
color: inherit;
}
.tocset .RktValLink, .tocset .RktStxLink, .tocset .RktModLink, .tocset .RktSym {
color: black;
font-weight: 400;
font-size: 0.9rem;
}
.tocset td a.tocviewselflink .RktValLink,
.tocset td a.tocviewselflink .RktStxLink,
.tocset td a.tocviewselflink .RktMod,
.tocset td a.tocviewselflink .RktSym {
font-weight: lighter;
color: white;
}
.RktRes {
color: #0000af;
}
.RktOut {
color: #960096;
}
.RktCmt {
color: #c2741f;
}
.RktVal {
color: #228b22;
}
/* ---------------------------------------- */
/* Some inline styles */
.together { /* for definitions grouped together in one box */
width: 100%;
border-top: 2px solid white;
}
tbody > tr:first-child > td > .together {
border-top: 0px; /* erase border on first instance of together */
}
.RktBlk {
white-space: pre;
text-align: left;
}
.highlighted {
font-size: 1rem;
background-color: #fee;
}
.defmodule {
font-family: 'Fira-Mono', monospace;
padding: 0.25rem 0.75rem 0.25rem 0.5rem;
margin-bottom: 1rem;
width: 100%;
background-color: #ebf0f4;
}
.defmodule a {
color: #444;
}
.defmodule td span.hspace:first-child {
position: absolute;
width: 0;
display: inline-block;
}
.defmodule .RpackageSpec .Smaller,
.defmodule .RpackageSpec .stt {
font-size: 1rem;
}
/* make parens ordinary color in defmodule */
.defmodule .RktPn {
color: inherit;
}
.specgrammar {
float: none;
padding-left: 1em;
}
.RBibliography td {
vertical-align: text-top;
padding-top: 1em;
}
.leftindent {
margin-left: 2rem;
margin-right: 0em;
}
.insetpara {
margin-left: 1em;
margin-right: 1em;
}
.SCodeFlow .Rfilebox {
margin-left: -1em; /* see 17.2 of guide, module languages */
}
.Rfiletitle {
text-align: right;
background-color: #eee;
}
.SCodeFlow .Rfiletitle {
border-top: 1px dotted gray;
border-right: 1px dotted gray;
}
.Rfilename {
border-top: 0;
border-right: 0;
padding-left: 0.5em;
padding-right: 0.5em;
background-color: inherit;
}
.Rfilecontent {
margin: 0.5em;
}
.RpackageSpec {
padding-right: 0;
}
/* ---------------------------------------- */
/* For background labels */
.RBackgroundLabel {
float: right;
width: 0px;
height: 0px;
}
.RBackgroundLabelInner {
position: relative;
width: 25em;
left: -25.5em;
top: 0.20rem; /* sensitive to monospaced font choice */
text-align: right;
z-index: 0;
font-weight: 300;
font-family: 'Fira-Mono', monospace;
font-size: 0.9rem;
color: gray;
}
.RpackageSpec .Smaller {
font-weight: 300;
font-family: 'Fira-Mono', monospace;
font-size: 0.9rem;
}
.RForeground {
position: relative;
left: 0px;
top: 0px;
z-index: 1;
}
/* ---------------------------------------- */
/* For section source modules & tags */
.RPartExplain {
background: #eee;
font-size: 0.9rem;
margin-top: 0.2rem;
padding: 0.2rem;
text-align: left;
}
+268
View File
@@ -0,0 +1,268 @@
/* For the Racket manual style */
AddOnLoad(function() {
/* Look for header elements that have x-source-module and x-part tag.
For those elements, add a hidden element that explains how to
link to the section, and set the element's onclick() to display
the explanation. */
var tag_names = ["h1", "h2", "h3", "h4", "h5"];
for (var j = 0; j < tag_names.length; j++) {
elems = document.getElementsByTagName(tag_names[j]);
for (var i = 0; i < elems.length; i++) {
var elem = elems.item(i);
AddPartTitleOnClick(elem);
}
}
})
// for copies that start in Racket code, strip out extra newlines
AddOnLoad(function() {
const codeBlocks = document.getElementsByClassName("SCodeFlow");
for (var i = 0; i < codeBlocks.length; i++) {
var codeBlock = codeBlocks[i];
console.log("add");
codeBlock.addEventListener('copy', function(e) {
var selection = window.getSelection();
var text = selection.toString();
var codeText = text.replace(/\n\n/g, '\n');
if (text != codeText) {
e.preventDefault();
e.clipboardData.setData('text/plain', codeText);
}
})
}
})
// cache of source urls
var cache = {};
function ParseSource(source, mod_path, single_collection) {
var source_url = new URL(source);
if (source_url.protocol == "github:") {
// browser URL parser only works with http(s) URLs
source_url = new URL("https" + source.substring(6));
var host = source_url.host;
var url_path = source_url.pathname.substring(1).split("/");
if (!(url_path.length >= 2)) return null;
var user = url_path.shift();
var repo = url_path.shift();
var branch = url_path.shift();
var source_path = url_path.join("/");
}
else if (("https:" == source_url.protocol) || ("git:" == source_url.protocol)) {
// browser URL parser only works with http(s) URLs
if ("git:" == source_url.protocol)
source_url = new URL("https" + source.substring(3));
var host = source_url.host;
var source_path = source_url.searchParams.get("path");
var branch = (source_url.hash || "#master").substring(1);
var url_path = source_url.pathname.substring(1).split("/");
if (url_path.length < 2) throw [source_url.pathname, url_path];
var user = url_path.shift();
var repo = url_path.shift();
var mtch = repo.match(/(.*)\.git$/);
if (mtch) repo = mtch[1];
}
else return null;
var mod_path_re = /^\(lib "(.+)"\)$/;
var mod_path_elems = mod_path && mod_path.match(mod_path_re)[1].split("/");
if (!user || !repo || !mod_path_elems)
return null;
if (single_collection)
mod_path_elems.shift();
var file_path = mod_path_elems.join("/");
if (source_path) {
file_path = source_path + "/" + file_path;
}
return { user: user,
repo: repo,
file_path: file_path,
branch: branch,
host: host };
}
function AddSourceElement(pkg_url, info) {
info.appendChild(document.createTextNode("Document source "));
var url_line = document.createElement("div");
var a = document.createElement("a");
a.href = pkg_url;
a.style.whiteSpace = "nowrap";
a.appendChild(document.createTextNode(pkg_url));
addSpan(url_line, "\xA0", "RktRdr");
url_line.appendChild(a);
info.appendChild(url_line);
}
var prefixes = { "github.com": "tree",
"gitlab.com": "-/blob" };
function AddSourceUrl(source, mod_path, collection, info) {
// multi is encoded as an array, empty as false
single_collection = (typeof collection === "string");
var parsed = source && mod_path && ParseSource(source, mod_path, single_collection);
if (!parsed) return;
prefix = prefixes.hasOwnProperty(parsed.host) && prefixes[parsed.host];
if (!prefix) return;
var correct_url = "https://" + [parsed.host, parsed.user, parsed.repo, prefix, parsed.branch, parsed.file_path].join("/");
if (info) AddSourceElement(correct_url, info);
}
function addSpan(dest, str, cn) {
var s = document.createElement("span");
s.className = cn;
s.style.whiteSpace = "nowrap";
s.appendChild(document.createTextNode(str));
dest.appendChild(s);
}
// test cases
if (false) {
console.log(ParseSource("git://gitlab.com/benn/foo?path=xxx",
'(lib "asn1/scribblings/asn1.scrbl")',
false))
console.log(ParseSource("github://github.com/carl-eastlund/mischief/master",
'(lib "asn1/scribblings/asn1.scrbl")',
false))
console.log(ParseSource("github://github.com/carl-eastlund/mischief/stable/dir",
'(lib "asn1/scribblings/asn1.scrbl")',
false))
console.log(ParseSource("git://github.com/racket/racket/?path=pkgs/racket-doc",
'(lib "asn1/scribblings/asn1.scrbl")',
false));
console.log(ParseSource("git://github.com/rmculpepper/asn1.git?path=asn1-doc",
'(lib "asn1/scribblings/asn1.scrbl")',
true));
console.log(ParseSource("git://github.com/rmculpepper/asn1",
'(lib "asn1/scribblings/asn1.scrbl")',
true));
console.log(ParseSource("git://github.com/rmculpepper/asn1",
'(lib "asn1/scribblings/asn1.scrbl")',
false));
}
function AddPartTitleOnClick(elem) {
var mod_path = elem.getAttribute("x-source-module");
var tag = elem.getAttribute("x-part-tag");
var source_pkg = elem.getAttribute("x-source-pkg");
// create here to share
var info = document.createElement("div");
// tag is not needed, but this way we can add the element in only one place
// avoid failing on browser that don't have `fetch`
if (mod_path && source_pkg && tag && window.fetch) {
var cached = cache[mod_path]
if (cached) {
AddSourceElement(cached[0], mod_path, cached[1], info);
}
else {
fetch("https://pkgs.racket-lang.org/pkg/" + source_pkg + ".json")
.then(function (response) { return response.json(); })
.then(function (data) {
var vers = data["versions"] || {};
var def = vers["default"] || {};
var source = def["source"] || undefined;
var collection = data["collection"];
if (source) {
cache[mod_path] = [source, collection];
AddSourceUrl(source, mod_path, collection, info);
}
});
}
}
if (mod_path && tag) {
// Might not be present:
var prefixes = elem.getAttribute("x-part-prefixes");
info.className = "RPartExplain";
/* The "top" tag refers to a whole document: */
var is_top = (tag == "\"top\"");
info.appendChild(document.createTextNode("Link to this "
+ (is_top ? "document" : "section")
+ " with "));
/* Break `secref` into two lines if the module path and tag
are long enough: */
var is_long = (is_top ? false : ((mod_path.length
+ tag.length
+ (prefixes ? (16 + prefixes.length) : 0))
> 60));
var line1 = document.createElement("div");
var line1x = ((is_long && prefixes) ? document.createElement("div") : line1);
var line2 = (is_long ? document.createElement("div") : line1);
/* Construct a `secref` call with suitable syntax coloring: */
addSpan(line1, "\xA0@", "RktRdr");
addSpan(line1, (is_top ? "other-doc" : "secref"), "RktSym");
addSpan(line1, "[", "RktPn");
if (!is_top)
addSpan(line1, tag, "RktVal");
if (is_long) {
/* indent additional lines: */
if (prefixes)
addSpan(line1x, "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0", "RktPn");
addSpan(line2, "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0", "RktPn");
}
if (prefixes) {
addSpan(line1x, " #:tag-prefixes ", "RktPn");
addSpan(line1x, "'", "RktVal");
addSpan(line1x, prefixes, "RktVal");
}
if (!is_top)
addSpan(line2, " #:doc ", "RktPn");
addSpan(line2, "'", "RktVal");
addSpan(line2, mod_path, "RktVal");
addSpan(line2, "]", "RktPn");
info.appendChild(line1);
if (is_long)
info.appendChild(line1x);
if (is_long)
info.appendChild(line2);
info.style.display = "none";
/* Add the new element afterthe header: */
var n = elem.nextSibling;
if (n)
elem.parentNode.insertBefore(info, n);
else
elem.parentNode.appendChild(info);
/* Clicking the information button shows the explanation element: */
const heading = elem.querySelector('.heading-source');
if (heading) {
heading.onclick = function () {
if (info.style.display === "none")
info.style.display = "block";
else
info.style.display = "none";
}
}
}
}
+815
View File
@@ -0,0 +1,815 @@
/* See the beginning of "scribble.css".
This file is used by the `scribble/manual` language, along with
"manual-racket.css". */
@import url("manual-fonts.css");
* {
margin: 0;
padding: 0;
}
@media all {html {font-size: 15px;}}
@media all and (max-width:940px){html {font-size: 14px;}}
@media all and (max-width:850px){html {font-size: 13px;}}
@media all and (max-width:830px){html {font-size: 12px;}}
@media all and (max-width:740px){html {font-size: 11px;}}
/* CSS seems backward: List all the classes for which we want a
particular font, so that the font can be changed in one place. (It
would be nicer to reference a font definition from all the places
that we want it.)
As you read the rest of the file, remember to double-check here to
see if any font is set. */
/* Monospace: */
.maincolumn, .refpara, .refelem, .tocset, .stt, .hspace, .refparaleft, .refelemleft {
font-family: 'Fira-Mono', monospace;
white-space: inherit;
font-size: 1rem;
}
/* Enable heading-source */
.button-group > .heading-source {
visibility: inherit;
cursor: pointer;
user-select: none;
color: gray;
}
/* embolden the "Racket Guide" and "Racket Reference" links on the TOC */
/* there isn't an obvious tag in the markup that designates the top TOC page, which is called "start.scrbl" */
/* nor a tag that designates these two links as special */
/* so we'll use this slightly tortured sibling selector that hooks onto the h1 tag */
h1[x-source-module='(lib "scribblings/main/start.scrbl")'] ~ table a[href="guide/index.html"],
h1[x-source-module='(lib "scribblings/main/start.scrbl")'] ~ table a[href="reference/index.html"] {
font-weight: bold;
}
h1 .stt {
font-size: 2.3rem;
/* prevent automatic bolding from h1 */
font-weight: 400;
}
.toptoclink .stt {
font-size: inherit;
}
.toclink .stt {
font-size: 90%;
}
.RpackageSpec .stt {
font-weight: 300;
font-family: 'Fira-Mono', monospace;
font-size: 0.9rem;
}
h2 .stt, h3 .stt, h4 .stt, h5 .stt {
color: #333;
font-size: 1.65rem;
font-weight: 400;
}
/* Serif: */
.main, .refcontent, .tocview, .tocsub, .sroman, i {
font-family: 'Charter-Racket', serif;
font-size: 1.18rem;
/* Don't use font-feature-settings with Charter,
it fouls up loading for reasons mysterious */
/* font-feature-settings: 'tnum' 1, 'liga' 0; */
}
/* Sans-serif: */
.version, .versionNoNav, .ssansserif, .navfamily, .famlink {
font-family: 'Fira', sans-serif;
}
/* used mostly for DrRacket menu commands */
.ssansserif {
font-family: 'Fira', sans-serif;
font-size: 0.9em;
}
.tocset .ssansserif {
font-size: 100%;
}
/* ---------------------------------------- */
p, .SIntrapara {
display: block;
margin: 0 0 1em 0;
line-height: 1.4;
}
.compact {
padding: 0 0 1em 0;
}
li {
list-style-position: outside;
margin-left: 1.2em;
}
h1, h2, h3, h4, h5, h6, h7, h8 {
font-family: 'Fira', sans-serif;
font-weight: 300;
font-size: 1.6rem;
color: #333;
margin-top: inherit;
margin-bottom: 1rem;
line-height: 1.25;
}
h2, h3, h4, h5, h6, h7, h8 {
border-top: 1px solid black;
}
h1 { /* per-page main title */
font-family: 'Cooper-Hewitt';
margin-top: 4rem;
font-size: 2.3rem;
font-weight: bold;
line-height: 1.2;
width: 90%;
/* a little nudge to make text visually lower than 4rem rule in left margin */
position: relative;
top: 6px;
}
h2, h3, h4, h5, h6, h7, h8 {
margin-top: 2em;
padding-top: 0.1em;
margin-bottom: 0.75em;
}
/* ---------------------------------------- */
/* Main */
body {
color: black;
background-color: white;
}
.maincolumn {
width: auto;
margin-top: 4rem;
margin-left: 17rem;
margin-right: 2rem;
margin-bottom: 10rem; /* to avoid fixed bottom nav bar */
max-width: 700px;
min-width: 370px; /* below this size, code samples don't fit */
}
a {
text-decoration: inherit;
}
a, .toclink, .toptoclink, .tocviewlink, .tocviewselflink, .tocviewtoggle, .plainlink,
.techinside, .techoutside:hover, .techinside:hover {
color: #07A;
}
a:hover {
text-decoration: underline;
}
/* ---------------------------------------- */
/* Navigation */
.navsettop, .navsetbottom {
left: 0;
width: 15rem;
height: 6rem;
font-family: 'Fira', sans-serif;
font-size: 0.9rem;
border-bottom: 0px solid hsl(216, 15%, 70%);
background-color: inherit;
padding: 0;
}
.navsettop {
position: fixed;
z-index: 2;
background: #a7b0be;
top: 0;
left: 0;
margin-bottom: 0;
border-bottom: 0;
}
.navsettop a, .navsetbottom a {
color: black;
}
.navsettop a:hover, .navsetbottom a:hover {
background: hsl(216, 78%, 95%);
text-decoration: none;
}
.navleft, .navright {
position: static;
float: none;
margin: 0;
white-space: normal;
}
.navleft a {
display: inline-block;
}
.navright a {
display: inline-block;
text-align: center;
}
.navleft a, .navright a, .navright span {
display: inline-block;
padding: 0.5rem;
min-width: 1rem;
}
.navright {
white-space: nowrap;
}
.navsetbottom {
display: none;
}
.nonavigation {
color: #889;
}
.searchform {
display: block;
margin: 0;
padding: 0;
border-bottom: 1px solid #eee;
height: 4rem;
}
.nosearchform {
margin: 0;
padding: 0;
height: 4rem;
}
.searchbox {
font-size: 0.9rem;
width: 12rem;
margin: 1rem;
padding: 0.25rem 0.4rem ;
vertical-align: middle;
background-color: white;
font-family: 'Fira-Mono', monospace;
}
#search_box {
font-family: 'Fira-Mono', monospace;
font-size: 1rem;
padding: 0.25rem 0.3rem ;
}
/* Default to local view. Global will specialize */
.plt_global_only { display: none; }
.plt_local_only { display: block; }
/* ---------------------------------------- */
/* Version */
.versionbox {
position: absolute;
float: none;
top: 0.25rem;
left: 17rem;
z-index: 11000;
height: 2em;
font-size: 70%;
font-weight: lighter;
width: inherit;
margin: 0;
}
.version, .versionNoNav {
font-size: inherit;
}
.version:before, .versionNoNav:before {
content: "v";
}
/* ---------------------------------------- */
/* Language Family */
.navfamily {
position: sticky;
top: 0;
left: 100%;
width: 13em;
margin-top: -4.75em;
margin-right: -15em;
font-size: 70%;
font-weight: lighter;
height: auto;
padding-top: 0.5em;
}
/* ---------------------------------------- */
/* Margin notes */
/* cancel scribble.css styles: */
.refpara, .refelem {
position: static;
float: none;
height: auto;
width: auto;
margin: 0;
}
.refcolumn {
position: static;
display: block;
width: auto;
font-size: inherit;
margin: 2rem;
margin-left: 2rem;
padding: 0.5em;
padding-left: 0.75em;
padding-right: 1em;
background: hsl(60, 29%, 94%);
border: 1px solid #ccb;
border-left: 0.4rem solid #ccb;
}
/* slightly different handling for margin-note* on narrow screens */
@media all and (max-width:1340px) {
span.refcolumn {
float: right;
width: 50%;
margin-left: 1rem;
margin-bottom: 0.8rem;
margin-top: 1.2rem;
}
.navfamily {
position: static;
margin: -4.75em 0em 0em 0em;
}
}
.refcontent, .refcontent p {
line-height: 1.5;
margin: 0;
}
.refcontent p + p {
margin-top: 1em;
}
.refcontent a {
font-weight: 400;
}
.refpara, .refparaleft {
top: -1em;
}
@media all and (max-width:600px) {
.refcolumn {
margin-left: 0;
margin-right: 0;
}
}
@media all and (min-width:1340px) {
.refcolumn {
margin: 0 -22.5rem 1rem 0;
float: right;
clear: right;
width: 18rem;
}
}
.refcontent {
font-family: 'Fira', sans-serif;
font-size: 1rem;
line-height: 1.6;
margin: 0 0 0 0;
}
.refparaleft, .refelemleft {
position: relative;
float: left;
right: 2em;
height: 0em;
width: 13em;
margin: 0em 0em 0em 0em;
display: contents;
}
.refcolumnleft {
background-color: hsl(60, 29%, 94%);
display: block;
position: relative;
width: 13em;
font-size: 85%;
border: 0.5em solid hsl(60, 29%, 94%);
margin: 0 0 0 0;
}
/* ---------------------------------------- */
/* Table of contents, left margin */
.tocset {
position: fixed;
z-index: 2;
overflow-y: scroll;
float: none;
left: 0;
top: 0rem;
bottom: 0;
width: 14rem;
padding: 0rem 0.5rem 0.5rem 0.5rem;
background-color: hsl(216, 15%, 70%);
border-top: 6rem solid hsl(216, 15%, 70%);
}
.tocset td {
vertical-align: text-top;
padding-bottom: 0.4rem;
padding-left: 0.2rem;
line-height: 1.1;
font-family: 'Fira', sans-serif;
}
.tocset td a {
color: black;
font-weight: 400;
}
.tocview {
text-align: left;
background-color: inherit;
margin-top: 1em;
}
.tocview td, .tocsub td {
line-height: 1.3;
}
.tocview table, .tocsub table {
width: 90%;
}
.tocset td a.tocviewselflink {
font-weight: lighter;
font-size: 110%; /* monospaced styles below don't need to enlarge */
color: white;
}
.tocviewselflink {
text-decoration: none;
}
.tocsub {
text-align: left;
margin-top: 0.5em;
background-color: inherit;
}
.tocviewlist, .tocsublist {
margin-left: 0.2em;
margin-right: 0.2em;
padding-top: 0.2em;
padding-bottom: 0.2em;
}
.tocviewlist table {
font-size: 82%;
}
.tocviewlisttopspace {
margin-bottom: 1em;
}
.tocviewsublist, .tocviewsublistonly, .tocviewsublisttop, .tocviewsublistbottom {
margin-left: 0.4em;
border-left: 1px solid #99a;
padding-left: 0.8em;
}
.tocviewsublist {
margin-bottom: 1em;
}
.tocviewsublist table,
.tocviewsublistonly table,
.tocviewsublisttop table,
.tocviewsublistbottom table,
table.tocsublist {
font-size: 1rem;
}
.tocviewsublist td,
.tocviewsublistbottom td,
.tocviewsublisttop td,
.tocsub td,
.tocviewsublistonly td {
font-size: 90%;
}
/* shrink the monospaced text (`stt`) within nav */
.tocviewsublist td .stt,
.tocviewsublistbottom td .stt,
.tocviewsublisttop td .stt,
.tocsub td .stt,
.tocviewsublistonly td .stt {
font-size: 95%;
}
.tocviewtoggle {
font-size: 75%; /* looks better, and avoids bounce when toggling sub-sections due to font alignments */
}
.tocsublist td {
padding-left: 0.5rem;
padding-top: 0.25rem;
text-indent: 0;
}
.tocsublinknumber {
font-size: 100%;
}
.tocsublink {
font-size: 82%;
text-decoration: none;
}
.tocsubseclink {
font-size: 100%;
text-decoration: none;
}
.tocsubnonseclink {
font-size: 82%;
text-decoration: none;
margin-left: 1rem;
padding-left: 0;
display: inline-block;
}
/* the label "on this page" */
.tocsubtitle {
display: block;
font-size: 62%;
font-family: 'Fira', sans-serif;
font-weight: bolder;
font-style: normal;
letter-spacing: 2px;
text-transform: uppercase;
margin: 0.5em;
}
.toptoclink {
font-weight: bold;
font-size: 110%;
margin-bottom: 0.5rem;
margin-top: 1.5rem;
display: inline-block;
}
.toclink {
font-size: inherit;
}
/* ---------------------------------------- */
/* Some inline styles */
.indexlink {
text-decoration: none;
}
pre {
margin-left: 2em;
}
blockquote {
margin-left: 2em;
margin-right: 2em;
margin-bottom: 1em;
}
.SCodeFlow {
border-left: 1px dotted black;
padding-left: 1em;
padding-right: 1em;
margin-top: 1em;
margin-bottom: 1em;
margin-left: 0em;
margin-right: 2em;
white-space: nowrap;
line-height: 1.5;
}
.SCodeFlow img {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
/* put a little air between lines of code sample */
/* Fira Mono appears taller than Source Code Pro */
.SCodeFlow td {
padding-bottom: 1px;
}
.boxed {
margin: 0;
margin-top: 2em;
padding: 0.25em;
padding-top: 0.3em;
padding-bottom: 0.4em;
background: #f3f3f3;
box-sizing:border-box;
border-top: 1px solid #99b;
background: hsl(216, 78%, 95%);
background: -moz-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%);
background: -webkit-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%);
background: -o-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%);
background: -ms-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%);
background: linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%);
}
blockquote > blockquote.SVInsetFlow {
/* resolves issue in e.g. /reference/notation.html */
margin-top: 0em;
}
.leftindent .SVInsetFlow { /* see e.g. section 4.5 of Racket Guide */
margin-top: 1em;
margin-bottom: 1em;
}
.SVInsetFlow a, .SCodeFlow a {
color: #07A;
}
.SubFlow {
display: block;
margin: 0em;
}
.boxed {
width: 100%;
background-color: inherit;
}
.techoutside { text-decoration: none; }
.SAuthorListBox {
position: static;
float: none;
font-family: 'Fira', sans-serif;
font-weight: 300;
font-size: 110%;
margin-top: 1rem;
margin-bottom: 2rem;
width: 30rem;
height: auto;
}
.author > a { /* email links within author block */
font-weight: inherit;
color: inherit;
}
.SAuthorList {
font-size: 82%;
}
.SAuthorList:before {
content: "by ";
}
.author {
display: inline;
white-space: nowrap;
}
/* phone + tablet styles */
@media all and (max-width:720px){
@media all and (max-width:720px){
@media all {html {font-size: 15px;}}
@media all and (max-width:700px){html {font-size: 14px;}}
@media all and (max-width:630px){html {font-size: 13px;}}
@media all and (max-width:610px){html {font-size: 12px;}}
@media all and (max-width:550px){html {font-size: 11px;}}
@media all and (max-width:520px){html {font-size: 10px;}}
.navsettop, .navsetbottom {
display: flex;
position: absolute;
width: 100%;
height: 4rem;
border: 0;
background-color: hsl(216, 15%, 70%);
align-items: center;
}
.tocsetoverlay .navsettop {
position: fixed;
}
.navleft {
flex: 1;
}
.searchform {
display: inline;
border: 0;
}
.searchbox {
margin-top: 0;
margin-bottom: 0;
}
.navleft .tocsettoggle {
display: initial;
}
.navright {
margin-right: 1.3rem;
border: 0px solid red;
}
.navsetbottom {
display: block;
margin-top: 8rem;
}
.tocset {
display: none;
border-top-width: 4rem;
}
.tocsetoverlay .tocset {
display: block;
}
.versionbox {
top: 4.5rem;
left: 1rem; /* same distance as main-column */
z-index: 1;
height: 2em;
font-size: 70%;
font-weight: lighter;
}
.navfamily {
position: static;
margin: -3.25em 0em 0em 0em;
}
.maincolumn {
margin-left: 1em;
margin-top: 7rem;
margin-bottom: 0rem;
}
}
}
/* print styles : hide the navigation elements */
@media print {
.tocset,
.navsettop,
.navsetbottom { display: none; }
.maincolumn {
width: auto;
margin-right: 13em;
margin-left: 0;
}
}
+251
View File
@@ -0,0 +1,251 @@
/* See the beginning of "scribble.css". */
/* Monospace: */
.RktIn, .RktRdr, .RktPn, .RktMeta,
.RktMod, .RktKw, .RktVar, .RktSym,
.RktRes, .RktOut, .RktCmt, .RktVal,
.RktBlk {
font-family: monospace;
white-space: inherit;
}
/* Serif: */
.inheritedlbl {
font-family: serif;
}
/* Sans-serif: */
.RBackgroundLabelInner {
font-family: sans-serif;
}
/* ---------------------------------------- */
/* Inherited methods, left margin */
.inherited {
width: 100%;
margin-top: 0.5em;
text-align: left;
background-color: #ECF5F5;
}
.inherited td {
font-size: 82%;
padding-left: 1em;
text-indent: -0.8em;
padding-right: 0.2em;
}
.inheritedlbl {
font-style: italic;
}
/* ---------------------------------------- */
/* Racket text styles */
.RktIn {
color: #cc6633;
background-color: #eeeeee;
white-space: pre;
}
.RktInBG {
background-color: #eeeeee;
}
.RktRdr {
}
.RktPn {
color: #843c24;
}
.RktMeta {
color: black;
}
.RktMod {
color: black;
}
.RktOpt {
color: black;
font-style: italic;
}
.RktKw {
color: black;
}
.RktErr {
color: red;
font-style: italic;
}
.RktVar {
color: #262680;
font-style: italic;
}
.RktSym {
color: #262680;
}
.RktSymDef { /* used with RktSym at def site */
}
.RktValLink {
text-decoration: none;
color: blue;
}
.RktValDef { /* used with RktValLink at def site */
}
.RktModLink {
text-decoration: none;
color: blue;
}
.RktStxLink {
text-decoration: none;
color: black;
}
.RktStxDef { /* used with RktStxLink at def site */
}
.RktRes {
color: #0000af;
}
.RktOut {
color: #960096;
}
.RktCmt {
color: #c2741f;
}
.RktVal {
color: #228b22;
}
/* ---------------------------------------- */
/* Some inline styles */
.together {
width: 100%;
}
.prototype, .argcontract, .RBoxed {
white-space: nowrap;
}
.prototype td {
vertical-align: text-top;
}
.RktBlk {
white-space: inherit;
text-align: left;
}
.RktBlk tr {
white-space: inherit;
}
.RktBlk td {
vertical-align: baseline;
white-space: inherit;
}
.argcontract td {
vertical-align: text-top;
}
.highlighted {
background-color: #ddddff;
}
.defmodule {
width: 100%;
background-color: #F5F5DC;
}
.specgrammar {
float: right;
}
.RBibliography td {
vertical-align: text-top;
}
.leftindent {
margin-left: 1em;
margin-right: 0em;
}
.insetpara {
margin-left: 1em;
margin-right: 1em;
}
.Rfilebox {
}
.Rfiletitle {
text-align: right;
margin: 0em 0em 0em 0em;
}
.Rfilename {
border-top: 1px solid #6C8585;
border-right: 1px solid #6C8585;
padding-left: 0.5em;
padding-right: 0.5em;
background-color: #ECF5F5;
}
.Rfilecontent {
margin: 0em 0em 0em 0em;
}
.RpackageSpec {
padding-right: 0.5em;
}
/* ---------------------------------------- */
/* For background labels */
.RBackgroundLabel {
float: right;
width: 0px;
height: 0px;
}
.RBackgroundLabelInner {
position: relative;
width: 25em;
left: -25.5em;
top: 0px;
text-align: right;
color: white;
z-index: 0;
font-weight: bold;
}
.RForeground {
position: relative;
left: 0px;
top: 0px;
z-index: 1;
}
/* ---------------------------------------- */
/* History */
.SHistory {
font-size: 82%;
}
+263
View File
@@ -0,0 +1,263 @@
// Common functionality for PLT documentation pages
// Page Parameters ------------------------------------------------------------
var plt_root_as_query = false;
function GetURL() {
return new URL(location);
}
function GetPageArgs() {
return GetURL().searchParams;
}
function GetPageQueryString() {
return GetPageArgs().toString();
}
function GetPageArg(key, def) {
return GetPageArgs().get(key) || def;
}
function MergePageArgsIntoLink(a) {
if ((GetPageArgs().size === 0 || !a.dataset.pltdoc) && !plt_root_as_query) return;
a.href = MergePageArgsIntoUrl(a.href);
}
function MergePageArgsIntoUrl(href) {
const url = new URL(href, window.location.href);
MergePageArgsIntoUrlObject(url);
return url.href;
}
function MergePageArgsIntoUrlObject(url) {
for (const [key, val] of GetPageArgs()) {
if (key[0] == "q") continue; // use "q" to mean "don't propagate automatcially"
if (url.searchParams.has(key)) continue;
url.searchParams.append(key, val)
}
if (plt_root_as_query && !url.searchParams.has("PLT_Root")) {
url.searchParams.append("PLT_Root", plt_root_as_query);
}
}
// Cookies --------------------------------------------------------------------
// Actually, try localStorage (a la HTML 5), first.
function GetCookie(key, def) {
try {
var v = localStorage[key];
if (!v) v = def;
return v;
} catch (e) {
var i, cookiestrs;
try {
if (document.cookie.length <= 0) return def;
cookiestrs = document.cookie.split(/; */);
} catch (e) { return def; }
for (i = 0; i < cookiestrs.length; i++) {
var cur = cookiestrs[i];
var eql = cur.indexOf('=');
if (eql >= 0 && cur.substring(0,eql) == key)
return unescape(cur.substring(eql+1));
}
return def;
}
}
function SetCookie(key, val) {
try {
localStorage[key] = val;
} catch(e) {
var d = new Date();
d.setTime(d.getTime()+(365*24*60*60*1000));
try {
document.cookie =
key + "=" + escape(val) + "; expires="+ d.toGMTString() + "; path=/";
} catch (e) {}
}
}
// note that this always stores a directory name, ending with a "/"
function SetPLTRoot(ver, relative) {
var root = location.protocol + "//" + location.host
+ NormalizePath(location.pathname.replace(/[^\/]*$/, relative));
if (location.protocol == "file:") {
// local storage or cookies are not going to work in modern browsers,
// so add a query parameter to all URLs
plt_root_as_query=root
} else {
SetCookie("PLT_Root."+ver, root);
}
}
// adding index.html works because of the above
function GotoPLTRoot(ver, root_relative, here_to_root_relative) {
// the relative path is optional, default goes to the toplevel start page
if (!root_relative) root_relative = "index.html";
if (here_to_root_relative == undefined) here_to_root_relative = "../"
var famroot = false;
if (root_relative == "index.html") {
famroot = (GetPageArg("fam", false) ? GetPageArg("famroot", false) : false)
if (famroot) {
root_relative = famroot + "/index.html";
}
}
var u = GetRootPath(ver);
if (u == null) {
if (famroot) {
location = MergePageArgsIntoUrl(here_to_root_relative + famroot + "/index.html");
return false;
}
// no cookie and no famroot => follow href, instead
return true;
}
location = MergePageArgsIntoUrl(u + root_relative);
return false;
}
function GetRootPath(ver) {
var u = GetCookie("PLT_Root."+ver, null);
if (u != null)
return u;
// via query argument? (especially for `file://` URLs)
u = GetPageArg("PLT_Root", null)
if (u != null)
return u;
// use root specified by local-redirect wrapper, if present
if (typeof user_doc_root != "undefined")
return user_doc_root;
return null;
}
// Utilities ------------------------------------------------------------------
var normalize_rxs = [/\/\/+/g, /\/\.(\/|$)/, /\/[^\/]*\/\.\.(\/|$)/];
function NormalizePath(path) {
var tmp, i;
for (i = 0; i < normalize_rxs.length; i++)
while ((tmp = path.replace(normalize_rxs[i], "/")) != path) path = tmp;
return path;
}
// `noscript' is problematic in some browsers (always renders as a
// block), use this hack instead (does not always work!)
// document.write("<style>mynoscript { display:none; }</style>");
// Interactions ---------------------------------------------------------------
function DoSearchKey(event, field, ver, top_path) {
var val = field.value;
if (event && event.key === 'Enter') {
var u = GetRootPath(ver);
if (u == null) u = top_path; // default: go to the top path
u += "search/index.html?q=" + encodeURIComponent(val);
u = MergePageArgsIntoUrl(u);
location = u;
return false;
}
return true;
}
function TocviewToggle(glyph, id) {
var s = document.getElementById(id).style;
var expand = s.display == "none";
s.display = expand ? "block" : "none";
glyph.innerHTML = expand ? "&#9660;" : "&#9658;";
}
function TocsetToggle() {
document.body.classList.toggle("tocsetoverlay");
}
// Page Init ------------------------------------------------------------------
// Note: could make a function that inspects and uses window.onload to chain to
// a previous one, but this file needs to be required first anyway, since it
// contains utilities for all other files.
var on_load_funcs = [];
function AddOnLoad(fun) { on_load_funcs.push(fun); }
window.onload = function() {
for (var i=0; i<on_load_funcs.length; i++) on_load_funcs[i]();
};
AddOnLoad(function(){
var links = document.getElementsByTagName("a");
for (var i=0; i<links.length; i++) MergePageArgsIntoLink(links[i]);
var label = GetPageArg("ctxtname",false);
if (!label) return;
var indicator = document.getElementById("contextindicator");
if (!indicator) return;
indicator.innerHTML = label;
indicator.style.display = "block";
});
// Pressing "S" or "s" focuses on the "...search manuals..." text field
AddOnLoad(function(){
window.addEventListener("keyup", function(e) {
if ((e.key === 's' || e.key === 'S') && e.target === document.body) {
var searchBox = document.getElementById('searchbox');
if (searchBox) {
searchBox.focus();
}
}
}, false);
});
AddOnLoad(function(){
var es = document.getElementsByClassName("family-navigation");
if (es.length > 0) {
var fams = es[0].dataset.familynav.split(/,/);
var fam = GetPageArg("famroot", false) && GetPageArg("fam", false);
if (!fam) fam = "Racket";
if (fams.indexOf(fam) == -1) {
for (var i=0; i < es.length; i++) {
es[i].style.display = "inline-block";
}
}
}
});
AddOnLoad(function(){
var es = document.getElementsByClassName("navfamily");
for (var i=0; i < es.length; i++) {
var e = es[i];
if (e.dataset.fam != undefined) {
var fams = e.dataset.fam.split(/,/);
var fam = GetPageArg("fam", false);
if (!fam) fam = "Racket";
var link = document.createElement('a');
var root = GetRootPath(e.dataset.version)
var family_url;
if (root == null) {
family_url = new URL(e.dataset.famPath + "family/index.html", window.location.href);
} else {
family_url = new URL(root + "family/index.html", window.location.href);
}
family_url.searchParams.append("qfrom", window.location.href)
MergePageArgsIntoUrlObject(family_url);
if (fams.indexOf(fam) == -1) {
var nav_as = document.createElement('div');
link.textContent = "navigating as " + fam;
link.href = family_url
nav_as.appendChild(link)
e.appendChild(nav_as)
} else {
var link = document.createElement('a');
var span = e.children[0]
link.textContent = span.textContent;
link.href = family_url
span.textContent = ''; // Clear span
e.removeChild(span);
link.appendChild(span);
e.appendChild(link);
}
}
}
});
+587
View File
@@ -0,0 +1,587 @@
/* This file is used by default by all Scribble documents.
See also "manual.css", which is added by default by the
`scribble/manual` language. */
/* CSS seems backward: List all the classes for which we want a
particular font, so that the font can be changed in one place. (It
would be nicer to reference a font definition from all the places
that we want it.)
As you read the rest of the file, remember to double-check here to
see if any font is set. */
/* Monospace: */
.maincolumn, .refpara, .refelem, .tocset, .stt, .hspace, .refparaleft, .refelemleft, .reffootnote {
font-family: monospace;
}
/* Serif: */
.main, .refcontent, .tocview, .tocsub, .sroman, i {
font-family: serif;
}
/* Sans-serif: */
.version, .versionNoNav, .ssansserif, .navfamily {
font-family: sans-serif;
}
.ssansserif {
font-size: 80%;
font-weight: bold;
}
/* Emphasis: alternate italics and normal as we nest */
.emph {
font-style: italic;
}
.emph .emph {
font-style: normal;
}
.emph .emph .emph {
font-style: italic;
}
.emph .emph .emph .emph {
font-style: normal;
}
.emph .emph .emph .emph .emph {
font-style: italic;
}
.emph .emph .emph .emph .emph .emph {
font-style: normal;
}
/* ---------------------------------------- */
p, .SIntrapara {
display: block;
margin: 1em 0;
}
h1 { /* per-page main title */
font-size: 1.5em;
margin-top: 0;
}
h2, h3, h4, h5, h6, h7, h8 {
margin-top: 1.75em;
margin-bottom: 0.5em;
}
h2 {
font-size: 1.17em;
}
h3 {
font-size: 1.00em;
}
h4 {
font-size: 0.83em;
}
.SSubSubSubSection {
font-weight: bold;
font-size: 0.83em; /* should match h5; from HTML 4 reference */
}
/* Needed for browsers like Opera, and eventually for HTML 4 conformance.
This means that multiple paragraphs in a table element do not have a space
between them. */
table p {
margin-top: 0;
margin-bottom: 0;
}
/* ---------------------------------------- */
/* Main */
body {
color: black;
background-color: #ffffff;
}
table td {
padding-left: 0;
padding-right: 0;
}
.maincolumn {
width: 43em;
margin-right: -40em;
margin-left: 15em;
}
.main {
text-align: left;
}
/* ---------------------------------------- */
/* Navigation */
.navsettop, .navsetbottom {
background-color: #f0f0e0;
padding: 0.25em 0 0.25em 0;
}
.navsettop {
margin-bottom: 1.5em;
border-bottom: 2px solid #e0e0c0;
}
.navsetbottom {
margin-top: 2em;
border-top: 2px solid #e0e0c0;
}
.navleft {
margin-left: 1ex;
position: relative;
float: left;
white-space: nowrap;
}
.navright {
margin-right: 1ex;
position: relative;
float: right;
white-space: nowrap;
}
.nonavigation {
color: #e0e0e0;
}
.navleft .tocsettoggle {
display: none;
}
.searchform {
display: inline;
margin: 0;
padding: 0;
}
.nosearchform {
display: none;
}
.searchbox {
width: 16em;
margin: 0px;
padding: 0px;
background-color: #eee;
border: 1px solid #ddd;
vertical-align: middle;
}
.searchbox::placeholder {
text-align: center;
}
#contextindicator {
position: fixed;
background-color: #c6f;
color: #000;
font-family: monospace;
font-weight: bold;
padding: 2px 10px;
display: none;
right: 0;
bottom: 0;
}
/* ---------------------------------------- */
/* Version */
.versionbox {
position: relative;
float: right;
left: 2em;
height: 0em;
width: 13em;
margin: 0em -13em 0em 0em;
}
.version {
font-size: small;
}
.versionNoNav {
font-size: xx-small; /* avoid overlap with author */
}
.version:before, .versionNoNav:before {
content: "Version ";
}
/* ---------------------------------------- */
/* Language Family */
.navfamily {
float: right;
white-space: nowrap;
left: 2em;
height: 0em;
width: 10.5em;
margin: 1.5em -13em 0em 0em;
font-size: small;
}
.docfamily:after {
content: " language family";
}
/* ---------------------------------------- */
/* Margin notes */
.refpara, .refelem, .reffootnote {
position: relative;
float: right;
left: 2em;
height: 0em;
width: 13em;
margin: 0em -13em 0em 0em;
}
.refpara, .refparaleft, .reffootnote {
top: -1em;
}
.refcolumn {
background-color: #F5F5DC;
display: block;
position: relative;
width: 13em;
font-size: 85%;
border: 0.5em solid #F5F5DC;
margin: 0 0 0 0;
white-space: normal; /* in case margin note is inside code sample */
}
.refcontent {
margin: 0 0 0 0;
}
.refcontent p {
margin-top: 0;
margin-bottom: 0;
}
.refparaleft, .refelemleft {
position: relative;
float: left;
right: 2em;
height: 0em;
width: 13em;
margin: 0em 0em 0em -13em;
}
.refcolumnleft {
background-color: #F5F5DC;
display: block;
position: relative;
width: 13em;
font-size: 85%;
border: 0.5em solid #F5F5DC;
margin: 0 0 0 0;
}
/* narrow display => inline right-hand margin notes */
@media screen and (max-width: 55em) {
.refpara, .refelem, .reffootnote {
all: unset !important;
display: block !important;
margin: 1em 0 !important;
padding: 0.75em !important;
}
}
/* ---------------------------------------- */
/* Table of contents, inline */
.toclink {
text-decoration: none;
color: blue;
font-size: 85%;
}
.toptoclink {
text-decoration: none;
color: blue;
font-weight: bold;
}
/* ---------------------------------------- */
/* Table of contents, left margin */
.tocset {
position: relative;
float: left;
width: 12.5em;
margin-right: 2em;
}
.tocset td {
vertical-align: text-top;
}
.tocview {
text-align: left;
background-color: #f0f0e0;
}
.tocsub {
text-align: left;
margin-top: 0.5em;
background-color: #f0f0e0;
}
.tocviewlist, .tocsublist {
margin-left: 0.2em;
margin-right: 0.2em;
padding-top: 0.2em;
padding-bottom: 0.2em;
}
.tocviewlist table {
font-size: 82%;
}
.tocviewlisttopspace {
margin-bottom: 1em;
}
.tocviewsublist, .tocviewsublistonly, .tocviewsublisttop, .tocviewsublistbottom {
margin-left: 0.4em;
border-left: 1px solid #bbf;
padding-left: 0.8em;
}
.tocviewsublist {
margin-bottom: 1em;
}
.tocviewsublist table,
.tocviewsublistonly table,
.tocviewsublisttop table,
.tocviewsublistbottom table {
font-size: 75%;
}
.tocviewtitle * {
font-weight: bold;
}
.tocviewlink {
text-decoration: none;
color: blue;
}
.tocviewselflink {
text-decoration: underline;
color: blue;
}
.tocviewtoggle {
text-decoration: none;
color: blue;
font-size: 75%; /* looks better, and avoids bounce when toggling sub-sections due to font alignments */
}
.tocsublist td {
padding-left: 1em;
text-indent: -1em;
}
.tocsublinknumber {
font-size: 82%;
}
.tocsublink {
font-size: 82%;
text-decoration: none;
}
.tocsubseclink {
font-size: 82%;
text-decoration: none;
}
.tocsubnonseclink {
font-size: 82%;
text-decoration: none;
padding-left: 0.5em;
}
.tocsubtitle {
font-size: 82%;
font-style: italic;
margin: 0.2em;
}
/* ---------------------------------------- */
/* Some inline styles */
.indexlink {
text-decoration: none;
}
.nobreak {
white-space: nowrap;
}
pre { margin-left: 2em; }
blockquote { margin-left: 2em; }
ol { list-style-type: decimal; }
ol ol { list-style-type: lower-alpha; }
ol ol ol { list-style-type: lower-roman; }
ol ol ol ol { list-style-type: upper-alpha; }
.SCodeFlow {
display: block;
margin-left: 1em;
margin-bottom: 0em;
margin-right: 1em;
margin-top: 0em;
white-space: nowrap;
}
.SVInsetFlow {
display: block;
margin-left: 0em;
margin-bottom: 0em;
margin-right: 0em;
margin-top: 0em;
}
.SubFlow {
display: block;
margin: 0em;
}
.boxed {
width: 100%;
background-color: #E8E8FF;
}
.hspace {
}
.slant {
font-style: oblique;
}
.badlink {
text-decoration: underline;
color: red;
}
.plainlink {
text-decoration: none;
color: blue;
}
.techoutside { text-decoration: underline; color: #b0b0b0; }
.techoutside:hover { text-decoration: underline; color: blue; }
/* .techinside:hover doesn't work with FF, .techinside:hover>
.techinside doesn't work with IE, so use both (and IE doesn't
work with inherit in the second one, so use blue directly) */
.techinside { color: black; }
.techinside:hover { color: blue; }
.techoutside:hover>.techinside { color: inherit; }
.SCentered {
text-align: center;
}
.imageleft {
float: left;
margin-right: 0.3em;
}
.Smaller {
font-size: 82%;
}
.Larger {
font-size: 122%;
}
/* A hack, inserted to break some Scheme ids: */
.mywbr {
display: inline-block;
height: 0;
width: 0;
font-size: 1px;
}
.compact li p {
margin: 0em;
padding: 0em;
}
.noborder img {
border: 0;
}
.SVerbatim {
white-space: nowrap;
}
.SAuthorListBox {
position: relative;
float: right;
left: 2em;
top: -2.25em;
height: 0em;
width: 13em;
margin: 0em -13em 0em 0em;
}
.SAuthorList {
font-size: 82%;
}
.SAuthorList:before {
content: "by ";
}
.author {
display: inline;
white-space: nowrap;
}
/* print styles : hide the navigation elements */
@media print {
.tocset,
.navsettop,
.navsetbottom { display: none; }
.maincolumn {
width: auto;
margin-right: 13em;
margin-left: 0;
}
}
/** Begin headings */
/* Hide the button group by default, but show them on hovering the heading title */
.button-group {
padding-left: 0.3em;
visibility: hidden;
position: absolute;
}
.heading:hover > .button-group {
visibility: visible;
}
.button-group > a {
margin: 0 0.25em;
}
.button-group > a, .button-group > a:hover {
text-decoration: none;
}
.heading-anchor {
font-size: 60%;
/* A trick to color an emoji from https://stackoverflow.com/questions/32413731/color-for-unicode-emoji */
color: transparent;
text-shadow: 0 0 0 gray;
vertical-align: 5%;
}
.heading-source {
visibility: hidden;
}
+14 -3
View File
@@ -60,7 +60,7 @@
(lambda (out) (displayln "changed" out))) (lambda (out) (displayln "changed" out)))
(define worktree-diff (git 'diff)) (define worktree-diff (git 'diff))
(check-true (regexp-match? #rx"-hello" worktree-diff)) (check-true (regexp-match? #rx"-hello" worktree-diff))
(check-true (regexp-match? #rx"\+changed" worktree-diff)) (check-true (regexp-match? #rx"[+]changed" worktree-diff))
(check-equal? (git 'diff '--cached) "") (check-equal? (git 'diff '--cached) "")
(define diff-output (open-output-string)) (define diff-output (open-output-string))
@@ -72,7 +72,7 @@
(check-equal? (git 'diff) "") (check-equal? (git 'diff) "")
(define cached-diff (git 'diff '--cached)) (define cached-diff (git 'diff '--cached))
(check-true (regexp-match? #rx"-hello" cached-diff)) (check-true (regexp-match? #rx"-hello" cached-diff))
(check-true (regexp-match? #rx"\+changed" cached-diff)) (check-true (regexp-match? #rx"[+]changed" cached-diff))
(git 'commit "prepare branches") (git 'commit "prepare branches")
(git 'checkout '-b "work") (git 'checkout '-b "work")
@@ -84,10 +84,21 @@
(check-equal? (file->string (build-path "sub" "hello.txt")) "work\n") (check-equal? (file->string (build-path "sub" "hello.txt")) "work\n")
(git 'checkout "master") (git 'checkout "master")
(check-equal? (file->string (build-path "sub" "hello.txt")) "hello\n") (check-equal? (file->string (build-path "sub" "hello.txt")) "changed\n")
(check-equal? (git-current-branch) "master") (check-equal? (git-current-branch) "master")
(check-not-false (member "work" (git 'branch))) (check-not-false (member "work" (git 'branch)))
;; Safe checkout must not overwrite an uncommitted tracked change.
(call-with-output-file (build-path "sub" "hello.txt")
#:exists 'truncate/replace
(lambda (out) (displayln "dirty" out)))
(check-exn exn:fail? (lambda () (git 'checkout "work")))
(check-equal? (git-current-branch) "master")
(check-equal? (file->string (build-path "sub" "hello.txt")) "dirty\n")
(call-with-output-file (build-path "sub" "hello.txt")
#:exists 'truncate/replace
(lambda (out) (displayln "changed" out)))
(git 'branch '-d "work") (git 'branch '-d "work")
(check-false (member "work" (git 'branch))) (check-false (member "work" (git 'branch)))
+33 -1
View File
@@ -8,6 +8,7 @@
(define origin (build-path tmp "origin.git")) (define origin (build-path tmp "origin.git"))
(define a (build-path tmp "a")) (define a (build-path tmp "a"))
(define b (build-path tmp "b")) (define b (build-path tmp "b"))
(define c (build-path tmp "c"))
(define (configure!) (define (configure!)
(git 'config "user.name" "Racket Git Test") (git 'config "user.name" "Racket Git Test")
@@ -30,9 +31,27 @@
(git 'remote 'add "origin" (path->string origin)) (git 'remote 'add "origin" (path->string origin))
(check-equal? (git 'remote) '("origin")) (check-equal? (git 'remote) '("origin"))
(check-equal? (git 'remote 'get-url "origin") (path->string origin)) (check-equal? (git 'remote 'get-url "origin") (path->string origin))
(define push-out (open-output-string))
(parameterize ([current-output-port push-out])
(git 'push)) (git 'push))
;; A successful push must remain safe when the returned credentials,
;; callbacks and options become eligible for collection.
(collect-garbage)
(collect-garbage)
(collect-garbage)
(check-true (regexp-match? #rx"\\[git\\] push origin/master"
(get-output-string push-out)))
(define quiet-out (open-output-string))
(parameterize ([current-output-port quiet-out])
(git 'push #:quiet #t))
(check-equal? (get-output-string quiet-out) ""))
(git-clone (path->string origin) b) (git-clone (path->string origin) b)
(define clone-quiet-out (open-output-string))
(parameterize ([current-output-port clone-quiet-out])
(git-clone (path->string origin) c #:quiet #t))
(check-equal? (get-output-string clone-quiet-out) "")
(parameterize ([current-directory b]) (parameterize ([current-directory b])
(configure!) (configure!)
(check-equal? (git-current-branch) "master") (check-equal? (git-current-branch) "master")
@@ -43,7 +62,11 @@
(git 'commit "two") (git 'commit "two")
(git 'push) (git 'push)
(git 'tag "v2") (git 'tag "v2")
(git 'push-tag "v2")) (git 'push-tag "v2")
(define push-tag-quiet-out (open-output-string))
(parameterize ([current-output-port push-tag-quiet-out])
(git 'push-tag "v2" #:quiet #t))
(check-equal? (get-output-string push-tag-quiet-out) ""))
(parameterize ([current-directory a]) (parameterize ([current-directory a])
(check-equal? (file->string "value.txt") "one\n") (check-equal? (file->string "value.txt") "one\n")
@@ -52,6 +75,15 @@
(check-true (git-clean?)) (check-true (git-clean?))
(git 'fetch) (git 'fetch)
(check-equal? (git 'tag) '("v2")) (check-equal? (git 'tag) '("v2"))
(define fetch-quiet-out (open-output-string))
(parameterize ([current-output-port fetch-quiet-out])
(git 'fetch #:quiet #t))
(check-equal? (get-output-string fetch-quiet-out) "")
(define pull-quiet-out (open-output-string))
(check-false
(parameterize ([current-output-port pull-quiet-out])
(git 'pull #:quiet #t)))
(check-equal? (get-output-string pull-quiet-out) "")
(call-with-output-file "local.txt" (call-with-output-file "local.txt"
#:exists 'truncate/replace #:exists 'truncate/replace