refactoring for first stable release
@ -1,167 +0,0 @@
|
||||
#lang racket/base
|
||||
(require racket/path racket/file compiler/cm file/cache sugar/coerce sugar/list "project.rkt" "rerequire.rkt" "debug.rkt" "file.rkt" racket/place "world.rkt")
|
||||
|
||||
;; The cache is a hash with paths as keys.
|
||||
;; The cache values are also hashes, with key/value pairs for that path.
|
||||
|
||||
(provide reset-cache preheat-cache cached-require paths->key key->source-path)
|
||||
|
||||
|
||||
(define (reset-cache [starting-dir (world:current-project-root)])
|
||||
(when (or (not (path-string? starting-dir)) (not (directory-exists? starting-dir)))
|
||||
(error 'reset-cache (format "~a is not a directory" starting-dir)))
|
||||
(for ([path (in-directory starting-dir)]
|
||||
#:when (and (directory-exists? path)
|
||||
(equal? (path->string (car (reverse (explode-path path)))) (world:current-cache-dir-name))))
|
||||
(message (format "removing cache directory: ~a" path))
|
||||
(delete-directory/files path)))
|
||||
|
||||
|
||||
(define (preheat-cache [starting-dir (world:current-project-root)])
|
||||
(when (or (not (path-string? starting-dir)) (not (directory-exists? starting-dir)))
|
||||
(error 'preheat-cache (format "~a is not a directory" starting-dir)))
|
||||
|
||||
(define max-places 8) ; number of parallel processes to spawn at a time
|
||||
|
||||
(define paths-that-should-be-cached (for/list ([path (in-directory starting-dir)]
|
||||
#:when (or (preproc-source? path)
|
||||
(markup-source? path)
|
||||
(markdown-source? path)
|
||||
(pagetree-source? path)))
|
||||
path))
|
||||
|
||||
;; if a file is already in the cache, no need to hit it again.
|
||||
;; this allows partially completed preheat jobs to resume.
|
||||
(define uncached-paths (filter
|
||||
(λ(path)
|
||||
;; #t = not cached; #f = already cached
|
||||
;; seems like it would be slow to load cache.rktd but it's not.
|
||||
(define-values (_ private-cache-dir) (make-cache-dirs path))
|
||||
(define cache-db-file (build-path private-cache-dir "cache.rktd"))
|
||||
(cond
|
||||
[(not (file-exists? cache-db-file)) #t]
|
||||
[else (define cache-db (file->value cache-db-file))
|
||||
(not (hash-has-key? cache-db (paths->key path)))])) paths-that-should-be-cached))
|
||||
|
||||
;; compile a path inside a place (= parallel processing)
|
||||
(define (path-into-place path)
|
||||
(message (format "caching: ~a" (find-relative-path starting-dir path)))
|
||||
(define p (place ch
|
||||
(define path (place-channel-get ch))
|
||||
(define-values (path-dir path-name _) (split-path path))
|
||||
(message (format "compiling: ~a" path-name))
|
||||
;; use #f to signal compile error. Otherwise allow errors to pass.
|
||||
(define result (with-handlers ([exn:fail? (λ _ (message (format "compile failed: ~a" path-name)) #f)])
|
||||
(path->hash path)))
|
||||
(place-channel-put ch result)))
|
||||
(place-channel-put p path)
|
||||
p)
|
||||
|
||||
;; compile the paths in groups, so they can be incrementally saved.
|
||||
;; that way, if there's a failure, the progress is preserved.
|
||||
;; but the slowest file in a group will prevent further progress.
|
||||
(for ([path-group (in-list (slice-at uncached-paths max-places))])
|
||||
(define path-places (map path-into-place path-group))
|
||||
(for ([path (in-list path-group)]
|
||||
[ppl (in-list path-places)])
|
||||
(define result (place-channel-get ppl))
|
||||
(when result ; #f is used to signal compile error
|
||||
(cache-ref! (paths->key path) (λ _ result))))))
|
||||
|
||||
|
||||
(define (paths->key source-path [template-path #f])
|
||||
;; key is list of file + mod-time pairs, use #f for missing
|
||||
(define path-strings (append (list source-path)
|
||||
(append (list (and template-path (or (get-source template-path) template-path))) ; if template has a source file, track that instead
|
||||
|
||||
(->list (get-directory-require-files source-path))))) ; is either list of files or (list #f)
|
||||
;; can't use relative paths for cache keys because source files include `here-path` which is absolute.
|
||||
;; problem is that cache could appear valid on another filesystem (based on relative pathnames & mod dates)
|
||||
;; but would actually be invalid (because the `here-path` names are wrong).
|
||||
(define poly-flag (and (has-inner-poly-ext? source-path) (world:current-poly-target)))
|
||||
(define pollen-env (getenv "POLLEN"))
|
||||
(define path+mod-time-pairs
|
||||
(map (λ(ps) (and ps (let ([cp (->complete-path ps)])
|
||||
(cons (path->string cp) (with-handlers ([exn:fail? (λ _ 0)])
|
||||
(file-or-directory-modify-seconds cp)))))) path-strings))
|
||||
(list* pollen-env poly-flag path+mod-time-pairs))
|
||||
|
||||
|
||||
(define (key->source-path key)
|
||||
(car (caddr key)))
|
||||
|
||||
(require sugar/test)
|
||||
(module-test-external
|
||||
(define ps "/users/nobody/project/source.html.pm")
|
||||
(check-equal? (key->source-path (paths->key ps)) ps))
|
||||
|
||||
(define-namespace-anchor anchor-to-this-namespace)
|
||||
|
||||
(define (path->hash path)
|
||||
;; new namespace forces dynamic-require to re-instantiate 'path'
|
||||
;; otherwise it gets cached in current namespace.
|
||||
(define drfs (get-directory-require-files path))
|
||||
(for-each managed-compile-zo (or drfs null))
|
||||
(define path-dir (dirname path))
|
||||
(apply hash
|
||||
(let ([doc-key (world:current-main-export)]
|
||||
[meta-key (world:current-meta-export)])
|
||||
(parameterize ([current-namespace (make-base-namespace)]
|
||||
[current-directory path-dir])
|
||||
;; I monkeyed around with using the metas submodule to pull out the metas (for speed)
|
||||
;; but in practice most files get their doc requested too.
|
||||
;; so it's just simpler to get both at once and be done with it.
|
||||
;; the savings of avoiding two cache fetches at the outset outweighs
|
||||
;; the benefit of not reloading doc when you just need metas.
|
||||
(namespace-attach-module (namespace-anchor->namespace anchor-to-this-namespace) 'pollen/world) ; brings in params
|
||||
(list doc-key (dynamic-require path doc-key) meta-key (dynamic-require path meta-key))))))
|
||||
|
||||
|
||||
(define (my-make-directory* dir)
|
||||
(let-values ([(base name dir?) (split-path dir)])
|
||||
(when (and (path? base) (not (directory-exists? base)))
|
||||
(my-make-directory* base))
|
||||
(unless (directory-exists? dir)
|
||||
(with-handlers ([exn:fail:filesystem:exists? void])
|
||||
(make-directory dir)))))
|
||||
|
||||
(define (make-cache-dirs path)
|
||||
(define path-dir (dirname path))
|
||||
(define cache-dir (build-path path-dir (world:current-cache-dir-name)))
|
||||
(define private-cache-dir (build-path cache-dir "private"))
|
||||
(my-make-directory* private-cache-dir) ; will also make cache-dir, if needed
|
||||
(values cache-dir private-cache-dir))
|
||||
|
||||
(define ram-cache (make-hash))
|
||||
|
||||
|
||||
(define (cache-ref! key path-hash-thunk)
|
||||
(define path (key->source-path key))
|
||||
(define-values (cache-dir private-cache-dir) (make-cache-dirs path))
|
||||
(define-values (path-dir path-filename _) (split-path path))
|
||||
(define dest-file (build-path cache-dir (format "~a.rktd" path-filename)))
|
||||
(cache-file dest-file
|
||||
#:exists-ok? #t
|
||||
key
|
||||
private-cache-dir
|
||||
(λ _
|
||||
(write-to-file (path-hash-thunk) dest-file #:exists 'replace))
|
||||
#:max-cache-size (world:current-compile-cache-max-size))
|
||||
(file->value dest-file))
|
||||
|
||||
|
||||
(define (cached-require path-string subkey)
|
||||
(define path (with-handlers ([exn:fail? (λ _ (raise-argument-error 'cached-require "valid path-string" path-string))])
|
||||
(->complete-path path-string)))
|
||||
|
||||
(when (not (file-exists? path))
|
||||
(raise-argument-error 'cached-require "path to existing file" path))
|
||||
|
||||
(cond
|
||||
[(world:current-compile-cache-active path)
|
||||
(define key (paths->key path))
|
||||
(hash-ref (hash-ref! ram-cache key (λ _
|
||||
(cache-ref! key (λ _ (path->hash path))))) subkey)]
|
||||
[else (parameterize ([current-namespace (make-base-namespace)])
|
||||
(namespace-attach-module (namespace-anchor->namespace anchor-to-this-namespace) 'pollen/world) ; brings in params
|
||||
(dynamic-require path subkey))]))
|
@ -1,8 +0,0 @@
|
||||
#lang racket/base
|
||||
(require pollen/main-base)
|
||||
(define+provide-module-begin-in-mode world:mode-preproc) ; because default mode in submodule is preproc
|
||||
|
||||
(module reader racket/base
|
||||
(require pollen/reader-base)
|
||||
;; because default mode in file is auto
|
||||
(define+provide-reader-in-mode world:mode-auto))
|
@ -1,8 +0,0 @@
|
||||
#lang racket/base
|
||||
(require pollen/main-base)
|
||||
|
||||
(define+provide-module-begin-in-mode world:mode-markdown)
|
||||
|
||||
(module reader racket/base
|
||||
(require pollen/reader-base)
|
||||
(define+provide-reader-in-mode world:mode-markdown))
|
@ -1,8 +0,0 @@
|
||||
#lang racket/base
|
||||
(require pollen/main-base)
|
||||
|
||||
(define+provide-module-begin-in-mode world:mode-markup)
|
||||
|
||||
(module reader racket/base
|
||||
(require pollen/reader-base)
|
||||
(define+provide-reader-in-mode world:mode-markup))
|
@ -0,0 +1,51 @@
|
||||
#lang racket/base
|
||||
(require racket/file
|
||||
sugar/define
|
||||
"private/cache-utils.rkt"
|
||||
"private/debug.rkt"
|
||||
"world.rkt")
|
||||
|
||||
;; The cache is a hash with paths as keys.
|
||||
;; The cache values are also hashes, with key/value pairs for that path.
|
||||
|
||||
(define+provide (reset-cache [starting-dir (world:current-project-root)])
|
||||
(unless (and (path-string? starting-dir) (directory-exists? starting-dir))
|
||||
(raise-argument-error 'reset-cache "path-string to existing directory" starting-dir))
|
||||
|
||||
(for ([path (in-directory starting-dir)]
|
||||
#:when (and (directory-exists? path)
|
||||
(equal? (path->string (car (reverse (explode-path path)))) (world:current-cache-dir-name))))
|
||||
(message (format "removing cache directory: ~a" path))
|
||||
(delete-directory/files path)))
|
||||
|
||||
(define-namespace-anchor cache-module-ns)
|
||||
(define cached-require-base
|
||||
(let ([ram-cache (make-hash)])
|
||||
(λ(path-or-path-string subkey caller-name)
|
||||
(define path (with-handlers ([exn:fail? (λ(e) (raise-argument-error caller-name "valid path or path-string" path-or-path-string))])
|
||||
(path->complete-path (if (path? path-or-path-string)
|
||||
path-or-path-string
|
||||
(string->path path-or-path-string)))))
|
||||
|
||||
(unless (file-exists? path)
|
||||
(raise-argument-error caller-name "path to existing file" path-or-path-string))
|
||||
|
||||
(cond
|
||||
[(world:current-compile-cache-active path)
|
||||
(define key (paths->key path))
|
||||
(hash-ref (hash-ref! ram-cache key (λ _ (cache-ref! key (λ _ (path->hash path))))) subkey)]
|
||||
[else (parameterize ([current-namespace (make-base-namespace)])
|
||||
(namespace-attach-module (namespace-anchor->namespace cache-module-ns) 'pollen/world) ; brings in params
|
||||
(dynamic-require path subkey))]))))
|
||||
|
||||
|
||||
(define+provide (cached-require path-string subkey)
|
||||
(cached-require-base path-string subkey 'cached-require))
|
||||
|
||||
|
||||
(define+provide (cached-doc path-string)
|
||||
(cached-require-base path-string (world:current-main-export) 'cached-doc))
|
||||
|
||||
|
||||
(define+provide (cached-metas path-string)
|
||||
(cached-require-base path-string (world:current-meta-export) 'cached-metas))
|
@ -0,0 +1,248 @@
|
||||
#lang racket/base
|
||||
(require xml txexpr racket/list sugar/list sugar/define sugar/test)
|
||||
(require "world.rkt"
|
||||
"private/whitespace.rkt")
|
||||
|
||||
|
||||
(define (->list/tx x)
|
||||
;; same as ->list but catches special case of single txexpr,
|
||||
;; which is itself a list, but in this case should be wrapped into a list,
|
||||
;; for use with append-map.
|
||||
(cond
|
||||
[(txexpr? x) (list x)]
|
||||
[(list? x) x]
|
||||
[else (list x)]))
|
||||
|
||||
(define decode-proc-output-contract (or/c txexpr-element? txexpr-elements?))
|
||||
(define identity (λ(x) x))
|
||||
|
||||
;; decoder wireframe
|
||||
(define+provide/contract (decode tx-in
|
||||
#:txexpr-tag-proc [txexpr-tag-proc identity]
|
||||
#:txexpr-attrs-proc [txexpr-attrs-proc identity]
|
||||
#:txexpr-elements-proc [txexpr-elements-proc identity]
|
||||
#:txexpr-proc [txexpr-proc identity]
|
||||
#:block-txexpr-proc [block-txexpr-proc identity]
|
||||
#:inline-txexpr-proc [inline-txexpr-proc identity]
|
||||
#:string-proc [string-proc identity]
|
||||
#:entity-proc [entity-proc identity]
|
||||
#:cdata-proc [cdata-proc identity]
|
||||
#:exclude-tags [excluded-tags empty]
|
||||
#:exclude-attrs [excluded-attrs empty])
|
||||
((xexpr/c)
|
||||
(#:txexpr-tag-proc (txexpr-tag? . -> . txexpr-tag?)
|
||||
#:txexpr-attrs-proc (txexpr-attrs? . -> . txexpr-attrs?)
|
||||
#:txexpr-elements-proc (txexpr-elements? . -> . txexpr-elements?)
|
||||
#:txexpr-proc (txexpr? . -> . decode-proc-output-contract)
|
||||
#:block-txexpr-proc (block-txexpr? . -> . decode-proc-output-contract)
|
||||
#:inline-txexpr-proc (txexpr? . -> . decode-proc-output-contract)
|
||||
#:string-proc (string? . -> . decode-proc-output-contract)
|
||||
#:entity-proc ((or/c symbol? valid-char?) . -> . decode-proc-output-contract)
|
||||
#:cdata-proc (cdata? . -> . decode-proc-output-contract)
|
||||
#:exclude-tags txexpr-tags?
|
||||
#:exclude-attrs txexpr-attrs?) . ->* . decode-proc-output-contract)
|
||||
(let loop ([x tx-in])
|
||||
(cond
|
||||
[(txexpr? x) (let-values([(tag attrs elements) (txexpr->values x)])
|
||||
(if (or (member tag excluded-tags) (ormap (λ(attr) (member attr excluded-attrs)) attrs))
|
||||
x ; because it's excluded
|
||||
;; we apply processing here rather than do recursive descent on the pieces
|
||||
;; because if we send them back through loop, certain element types are ambiguous
|
||||
;; e.g., ((p "foo")) tests out as both txexpr-attrs and txexpr-elements
|
||||
(let ([decoded-txexpr
|
||||
(apply make-txexpr (list (txexpr-tag-proc tag)
|
||||
(txexpr-attrs-proc attrs)
|
||||
(txexpr-elements-proc (append-map (compose1 ->list/tx loop) elements))))])
|
||||
((compose1 txexpr-proc (if (block-txexpr? decoded-txexpr)
|
||||
block-txexpr-proc
|
||||
inline-txexpr-proc)) decoded-txexpr))))]
|
||||
[(string? x) (string-proc x)]
|
||||
[(or (symbol? x) (valid-char? x)) (entity-proc x)]
|
||||
[(cdata? x) (cdata-proc x)]
|
||||
[else (error "decode: can't decode" x)])))
|
||||
|
||||
(module-test-external
|
||||
(require racket/list txexpr racket/function)
|
||||
(define (doubler x) (list x x))
|
||||
(define (doubletag x) (txexpr (string->symbol (format "~a~a" (get-tag x) (get-tag x))) (get-attrs x) (get-elements x)))
|
||||
(check-equal? (decode #:txexpr-elements-proc identity '(p "foo")) '(p "foo"))
|
||||
;; can't use doubler on txexpr-elements because it needs a list, not list of lists
|
||||
(check-equal? (decode #:txexpr-elements-proc (λ(elems) (append elems elems)) '(p "foo")) '(p "foo" "foo"))
|
||||
(check-equal? (decode #:block-txexpr-proc identity '(p "foo")) '(p "foo"))
|
||||
(check-equal? (decode #:block-txexpr-proc doubler '(p "foo")) (list '(p "foo") '(p "foo")))
|
||||
(check-equal? (decode #:block-txexpr-proc doubler '(p "foo")) (list '(p "foo") '(p "foo")))
|
||||
(check-equal? (decode #:txexpr-proc doubletag '(root (p "foo") (b "bar"))) '(rootroot (pp "foo") (bb "bar")))
|
||||
(check-equal? (decode #:block-txexpr-proc doubletag '(root (p "foo") (b "bar"))) '(rootroot (pp "foo") (b "bar")))
|
||||
(check-equal? (decode #:inline-txexpr-proc doubletag '(root (p "foo") (b "bar"))) '(root (p "foo") (bb "bar")))
|
||||
(check-equal? (decode #:inline-txexpr-proc identity '(p (span "foo"))) '(p (span "foo")))
|
||||
(check-equal? (decode #:inline-txexpr-proc doubler '(p (span "foo"))) '(p (span "foo") (span "foo")))
|
||||
(check-equal? (decode #:string-proc identity '(p (span "foo"))) '(p (span "foo")))
|
||||
(check-equal? (decode #:string-proc doubler '(p (span "foo"))) '(p (span "foo" "foo")))
|
||||
(check-equal? (decode #:entity-proc identity '(p (span "foo" 'amp))) '(p (span "foo" 'amp)))
|
||||
(check-equal? (decode #:entity-proc identity '(p 42)) '(p 42))
|
||||
(check-equal? (decode #:entity-proc doubler '(p 42)) '(p 42 42))
|
||||
(check-equal? (decode #:entity-proc identity '(p amp)) '(p amp))
|
||||
;; next text doesn't work because list of symbol elements is ambiguous with tagged X-expression
|
||||
;; is there a general patch for this? maybe, but for now it's better to not patch selectively
|
||||
;; otherwise ambiguous expressions will have erratic misbehavior (instead of merely consistent misbehavior)
|
||||
;;(check-equal? (decode #:entity-proc doubler '(p amp)) '(p amp amp))
|
||||
(check-equal? (decode-elements #:string-proc identity '("foo")) '("foo"))
|
||||
(check-equal? (decode-elements #:string-proc doubler '("foo")) '("foo" "foo")))
|
||||
|
||||
;; it would be nice to not repeat this, but with all the keywords, it's simpler to repeat than do a macro
|
||||
(define+provide/contract decode-elements
|
||||
((txexpr-elements?)
|
||||
(#:txexpr-tag-proc (txexpr-tag? . -> . txexpr-tag?)
|
||||
#:txexpr-attrs-proc (txexpr-attrs? . -> . txexpr-attrs?)
|
||||
#:txexpr-elements-proc (txexpr-elements? . -> . txexpr-elements?)
|
||||
#:txexpr-proc (txexpr? . -> . decode-proc-output-contract)
|
||||
#:block-txexpr-proc (block-txexpr? . -> . decode-proc-output-contract)
|
||||
#:inline-txexpr-proc (txexpr? . -> . decode-proc-output-contract)
|
||||
#:string-proc (string? . -> . decode-proc-output-contract)
|
||||
#:entity-proc ((or/c symbol? valid-char?) . -> . decode-proc-output-contract)
|
||||
#:cdata-proc (cdata? . -> . decode-proc-output-contract)
|
||||
#:exclude-tags txexpr-tags?
|
||||
#:exclude-attrs txexpr-attrs?) . ->* . decode-proc-output-contract)
|
||||
(make-keyword-procedure
|
||||
(λ (kws kwargs . args)
|
||||
(define temp-tag (gensym "temp-tag"))
|
||||
(define elements (car args))
|
||||
(define decode-result (keyword-apply decode kws kwargs (list (cons temp-tag elements))))
|
||||
(get-elements decode-result))))
|
||||
|
||||
|
||||
(define+provide/contract (block-txexpr? x)
|
||||
(any/c . -> . boolean?)
|
||||
;; Mostly this is used inside `decode`,
|
||||
;; so rather than test for `txexpr?` at the beginning (which is potentially slow)
|
||||
;; just look at the tag.
|
||||
(and (pair? x)
|
||||
(memq (get-tag x) (world:current-block-tags))
|
||||
#t))
|
||||
|
||||
|
||||
(define+provide/contract (decode-linebreaks elems [maybe-linebreak-proc '(br)]
|
||||
#:separator [newline (world:current-linebreak-separator)])
|
||||
((txexpr-elements?) ((or/c txexpr-element? (txexpr-element? txexpr-element? . -> . txexpr-element?)) #:separator string?) . ->* . txexpr-elements?)
|
||||
(define linebreak-proc (if (procedure? maybe-linebreak-proc)
|
||||
maybe-linebreak-proc
|
||||
(λ (e1 e2) maybe-linebreak-proc)))
|
||||
(define elems-vec (list->vector elems))
|
||||
(filter identity
|
||||
(for/list ([(item i) (in-indexed elems-vec)])
|
||||
(cond
|
||||
[(or (= i 0) (= i (sub1 (vector-length elems-vec)))) item] ; pass through first & last items
|
||||
[(equal? item newline)
|
||||
(let ([prev (vector-ref elems-vec (sub1 i))]
|
||||
[next (vector-ref elems-vec (add1 i))])
|
||||
;; only convert if neither adjacent tag is a block
|
||||
;; (because blocks automatically force a newline before & after)
|
||||
(if (or (block-txexpr? prev) (block-txexpr? next))
|
||||
#f ; flag for filtering
|
||||
(linebreak-proc prev next)))]
|
||||
[else item]))))
|
||||
|
||||
(module-test-external
|
||||
(check-equal? (decode-linebreaks '("foo" "\n" "bar")) '("foo" (br) "bar"))
|
||||
(check-equal? (decode-linebreaks '("\n" "foo" "\n" "bar" "\n")) '("\n" "foo" (br) "bar" "\n"))
|
||||
(check-equal? (decode-linebreaks '((p "foo") "\n" (p "bar"))) '((p "foo") (p "bar")))
|
||||
(check-equal? (decode-linebreaks '("foo" "\n" (p "bar"))) '("foo" (p "bar")))
|
||||
(check-equal? (decode-linebreaks '("foo" "moo" "bar")) '("foo" "moo" "bar"))
|
||||
(check-equal? (decode-linebreaks '("foo" "moo" "bar") "moo") '("foo" "moo" "bar"))
|
||||
(check-equal? (decode-linebreaks '("foo" "\n\n" "bar")) '("foo" "\n\n" "bar")))
|
||||
|
||||
|
||||
;; Find adjacent newline characters in a list and merge them into one item
|
||||
;; Scribble, by default, makes each newline a separate list item.
|
||||
(define+provide/contract (merge-newlines x)
|
||||
(txexpr-elements? . -> . txexpr-elements?)
|
||||
(define newline-pat (regexp (format "^~a+$" (world:current-newline))))
|
||||
(define (newlines? x) (and (string? x) (regexp-match newline-pat x)))
|
||||
(define (merge-if-newlines xs)
|
||||
(if (newlines? (car xs))
|
||||
(list (apply string-append xs))
|
||||
xs))
|
||||
(let loop ([x x])
|
||||
(if (pair? x)
|
||||
(let ([xs (map loop x)])
|
||||
(append-map merge-if-newlines (slicef xs newlines?)))
|
||||
x)))
|
||||
|
||||
(module-test-external
|
||||
(require racket/list)
|
||||
(check-equal? (merge-newlines empty) empty)
|
||||
(check-equal? (merge-newlines '(p "\n" "\n" "foo" "\n" "\n\n" "bar" (em "\n" "\n" "\n")))
|
||||
'(p "\n\n" "foo" "\n\n\n" "bar" (em "\n\n\n"))))
|
||||
|
||||
|
||||
|
||||
;; detect paragraphs
|
||||
;; todo: unit tests
|
||||
(define+provide/contract (decode-paragraphs elements [maybe-wrap-proc 'p]
|
||||
#:linebreak-proc [linebreak-proc decode-linebreaks]
|
||||
#:force? [force-paragraph #f])
|
||||
((txexpr-elements?) ((or/c txexpr-tag? ((listof xexpr?) . -> . txexpr?))
|
||||
#:linebreak-proc (txexpr-elements? . -> . txexpr-elements?)
|
||||
#:force? boolean?)
|
||||
. ->* . txexpr-elements?)
|
||||
|
||||
(define (prep-paragraph-flow elems)
|
||||
(linebreak-proc (merge-newlines (trimf elems whitespace?))))
|
||||
|
||||
(define (paragraph-break? x)
|
||||
(define paragraph-separator (world:current-paragraph-separator))
|
||||
(define paragraph-pattern (pregexp (format "^~a+$" paragraph-separator)))
|
||||
(and (string? x) (regexp-match paragraph-pattern x)))
|
||||
|
||||
(define (explicit-or-implicit-paragraph-break? x)
|
||||
(or (paragraph-break? x) (block-txexpr? x)))
|
||||
|
||||
(define wrap-proc (if (procedure? maybe-wrap-proc)
|
||||
maybe-wrap-proc
|
||||
(λ(elems) (list* maybe-wrap-proc elems))))
|
||||
|
||||
(define (wrap-paragraph elems)
|
||||
(if (andmap block-txexpr? elems)
|
||||
elems ; leave a series of block xexprs alone
|
||||
(list (wrap-proc elems)))) ; otherwise wrap in p tag
|
||||
|
||||
(let ([elements (prep-paragraph-flow elements)])
|
||||
(if (ormap explicit-or-implicit-paragraph-break? elements) ; need this condition to prevent infinite recursion
|
||||
;; use append-map on wrap-paragraph rather than map to permit return of multiple elements
|
||||
(append-map wrap-paragraph (append-map (λ(es) (filter-split es paragraph-break?)) (slicef elements block-txexpr?))) ; split into ¶¶, using both implied and explicit paragraph breaks
|
||||
(if force-paragraph
|
||||
(append-map wrap-paragraph (slicef elements block-txexpr?)) ; upconverts non-block elements to paragraphs
|
||||
elements))))
|
||||
|
||||
(module-test-external
|
||||
(check-equal? (decode-paragraphs '("First para" "\n\n" "Second para"))
|
||||
'((p "First para") (p "Second para")))
|
||||
(check-equal? (decode-paragraphs '("First para" "\n\n" "Second para" "\n" "Second line"))
|
||||
'((p "First para") (p "Second para" (br) "Second line")))
|
||||
(check-equal? (decode-paragraphs '("First para" "\n\n" (div "Second block")))
|
||||
'((p "First para") (div "Second block")))
|
||||
(check-equal? (decode-paragraphs '((div "First block") "\n\n" (div "Second block")))
|
||||
'((div "First block") (div "Second block")))
|
||||
(check-equal? (decode-paragraphs '("First para" "\n\n" "Second para") 'ns:p)
|
||||
'((ns:p "First para") (ns:p "Second para")))
|
||||
(check-equal? (decode-paragraphs '("First para" "\n\n" "Second para" "\n" "Second line")
|
||||
#:linebreak-proc (λ(x) (decode-linebreaks x '(newline))))
|
||||
'((p "First para") (p "Second para" (newline) "Second line")))
|
||||
(check-equal? (decode-paragraphs '("foo" "\n\n" (div "bar") (div "zam")))
|
||||
'((p "foo") (div "bar") (div "zam")))
|
||||
(check-equal? (decode-paragraphs '("foo" "\n\n" (div "bar") "\n\n" (div "zam")))
|
||||
'((p "foo") (div "bar") (div "zam")))
|
||||
|
||||
(check-equal? (decode-paragraphs '("foo")) '("foo"))
|
||||
(check-equal? (decode-paragraphs '("foo") #:force? #t) '((p "foo")))
|
||||
(check-equal? (decode-paragraphs '((div "foo"))) '((div "foo")))
|
||||
(check-equal? (decode-paragraphs '((div "foo")) #:force? #t) '((div "foo")))
|
||||
(check-equal? (decode-paragraphs '("foo" "\n\n" (div "bar"))) '((p "foo") (div "bar")))
|
||||
(check-equal? (decode-paragraphs '("foo" (div "bar"))) '((p "foo") (div "bar")))
|
||||
(check-equal? (decode-paragraphs '("foo" (div "bar")) #:force? #t) '((p "foo") (div "bar")))
|
||||
(check-equal? (decode-paragraphs '("foo" (div "bar") "zam")) '((p "foo") (div "bar") (p "zam")))
|
||||
(check-equal? (decode-paragraphs '("foo" (span "zing") (div "bar") "zam")) '((p "foo" (span "zing")) (div "bar") (p "zam")))
|
||||
(check-equal? (decode-paragraphs '("foo" (span "zing") (div "bar") "zam") #:force? #t) '((p "foo" (span "zing")) (div "bar") (p "zam"))))
|
||||
|
||||
(define+provide detect-paragraphs decode-paragraphs) ; bw compat
|
||||
(define+provide detect-linebreaks decode-linebreaks) ; bw compat
|
@ -0,0 +1,52 @@
|
||||
#lang racket/base
|
||||
(require sugar/test racket/require (matching-identifiers-in #rx"-source\\?$" "private/file-utils.rkt")
|
||||
(matching-identifiers-in #rx"-source-path$" "private/file-utils.rkt")
|
||||
(matching-identifiers-in #rx"^get.*-source$" "private/file-utils.rkt")
|
||||
(only-in "private/file-utils.rkt" ->output-path))
|
||||
(provide (all-from-out "private/file-utils.rkt"))
|
||||
|
||||
;; do the tests here to verify that the right functions are available through file.rkt
|
||||
(module-test-external
|
||||
(require sugar/coerce pollen/world)
|
||||
(check-true (preproc-source? "foo.pp"))
|
||||
(check-false (preproc-source? "foo.bar"))
|
||||
(check-false (preproc-source? #f))
|
||||
(check-equal? (->preproc-source-path (->path "foo.pp")) (->path "foo.pp"))
|
||||
(check-equal? (->preproc-source-path (->path "foo.html")) (->path "foo.html.pp"))
|
||||
(check-equal? (->preproc-source-path "foo") (->path "foo.pp"))
|
||||
(check-equal? (->preproc-source-path 'foo) (->path "foo.pp"))
|
||||
|
||||
(check-true (pagetree-source? (format "foo.~a" (world:current-pagetree-source-ext))))
|
||||
(check-false (pagetree-source? (format "~a.foo" (world:current-pagetree-source-ext))))
|
||||
(check-false (pagetree-source? #f))
|
||||
|
||||
(check-true (markup-source? "foo.pm"))
|
||||
(check-false (markup-source? "foo.p"))
|
||||
(check-false (markup-source? #f))
|
||||
(check-equal? (->markup-source-path (->path "foo.pm")) (->path "foo.pm"))
|
||||
(check-equal? (->markup-source-path (->path "foo.html")) (->path "foo.html.pm"))
|
||||
(check-equal? (->markup-source-path "foo") (->path "foo.pm"))
|
||||
(check-equal? (->markup-source-path 'foo) (->path "foo.pm"))
|
||||
|
||||
(check-true (markdown-source? "foo.html.pmd"))
|
||||
(check-false (markdown-source? "foo.html"))
|
||||
(check-false (markdown-source? #f))
|
||||
(check-equal? (->markdown-source-path (->path "foo.pmd")) (->path "foo.pmd"))
|
||||
(check-equal? (->markdown-source-path (->path "foo.html")) (->path "foo.html.pmd"))
|
||||
(check-equal? (->markdown-source-path "foo") (->path "foo.pmd"))
|
||||
(check-equal? (->markdown-source-path 'foo) (->path "foo.pmd"))
|
||||
|
||||
(check-false (get-source 42))
|
||||
(check-equal? (->output-path (->path "foo.pmap")) (->path "foo.pmap"))
|
||||
(check-equal? (->output-path "foo.html") (->path "foo.html"))
|
||||
(check-equal? (->output-path 'foo.html.p) (->path "foo.html"))
|
||||
(check-equal? (->output-path (->path "/Users/mb/git/foo.html.p")) (->path "/Users/mb/git/foo.html"))
|
||||
(check-equal? (->output-path "foo.xml.p") (->path "foo.xml"))
|
||||
(check-equal? (->output-path 'foo.barml.p) (->path "foo.barml"))
|
||||
|
||||
(check-equal? (->output-path 'foo_html.p) (->path "foo.html"))
|
||||
(check-equal? (->output-path (->path "/Users/mb/git/foo_html.p")) (->path "/Users/mb/git/foo.html"))
|
||||
(check-equal? (->output-path "foo_xml.p") (->path "foo.xml"))
|
||||
(check-equal? (->output-path 'foo_barml.p) (->path "foo.barml"))
|
||||
(check-equal? (->output-path "foo.poly.pm") (->path "foo.html"))
|
||||
(check-equal? (->output-path "foo_poly.pp") (->path "foo.html")))
|
@ -0,0 +1,7 @@
|
||||
#lang info
|
||||
(define scribblings '(("scribblings/pollen.scrbl" (multi-page))))
|
||||
(define raco-commands '(("pollen" (submod pollen/private/command raco) "issue Pollen command" #f)))
|
||||
(define compile-omit-paths '("test" "tools" "server-extras" "scribblings/third-tutorial-files"))
|
||||
;; it's redundant to test "pollen.scrbl" because it incorporates the other scribble sources by reference
|
||||
(define test-omit-paths '("test/data" "tools" "server-extras" "scribblings/third-tutorial-files" "scribblings/pollen.scrbl"))
|
||||
(define module-suffixes '(#"pp" #"pm" #"pmd" #"ptree"))
|
@ -0,0 +1,8 @@
|
||||
#lang racket/base
|
||||
(require "private/main-base.rkt")
|
||||
|
||||
(define+provide-module-begin-in-mode world:mode-preproc) ; because default mode in submodule is preproc
|
||||
|
||||
(module reader racket/base
|
||||
(require pollen/private/reader-base)
|
||||
(define+provide-reader-in-mode world:mode-auto)) ; because default mode in file is auto
|
@ -0,0 +1,8 @@
|
||||
#lang racket/base
|
||||
(require "private/main-base.rkt")
|
||||
|
||||
(define+provide-module-begin-in-mode world:mode-markdown)
|
||||
|
||||
(module reader racket/base
|
||||
(require pollen/private/reader-base)
|
||||
(define+provide-reader-in-mode world:mode-markdown))
|
@ -0,0 +1,8 @@
|
||||
#lang racket/base
|
||||
(require "private/main-base.rkt")
|
||||
|
||||
(define+provide-module-begin-in-mode world:mode-markup)
|
||||
|
||||
(module reader racket/base
|
||||
(require pollen/private/reader-base)
|
||||
(define+provide-reader-in-mode world:mode-markup))
|
@ -0,0 +1,3 @@
|
||||
#lang racket/base
|
||||
(require pollen/unstable/mb)
|
||||
(provide smart-quotes smart-dashes)
|
@ -0,0 +1,8 @@
|
||||
#lang racket/base
|
||||
(require "private/main-base.rkt")
|
||||
|
||||
(define+provide-module-begin-in-mode world:mode-preproc)
|
||||
|
||||
(module reader racket/base
|
||||
(require pollen/private/reader-base)
|
||||
(define+provide-reader-in-mode world:mode-preproc))
|
@ -0,0 +1,79 @@
|
||||
#lang racket/base
|
||||
(require "file-utils.rkt" "../world.rkt" "project.rkt" file/cache racket/file sugar/coerce compiler/cm)
|
||||
(provide (all-defined-out))
|
||||
|
||||
(define (paths->key source-path [template-path #f])
|
||||
;; key is list of file + mod-time pairs, use #f for missing
|
||||
(define path-strings (append (list source-path)
|
||||
;; if template has a source file, track that instead
|
||||
(append (list (and template-path (or (get-source template-path) template-path)))
|
||||
;; is either list of files or (list #f)
|
||||
(->list (get-directory-require-files source-path)))))
|
||||
;; can't use relative paths for cache keys because source files include `here-path` which is absolute.
|
||||
;; problem is that cache could appear valid on another filesystem (based on relative pathnames & mod dates)
|
||||
;; but would actually be invalid (because the `here-path` names are wrong).
|
||||
(define poly-flag (and (has-inner-poly-ext? source-path) (world:current-poly-target)))
|
||||
(define pollen-env (getenv world:env-name))
|
||||
(define path+mod-time-pairs
|
||||
(map (λ(ps) (and ps (let ([cp (->complete-path ps)])
|
||||
(cons (path->string cp) (with-handlers ([exn:fail? (λ _ 0)])
|
||||
(file-or-directory-modify-seconds cp)))))) path-strings))
|
||||
(list* pollen-env poly-flag path+mod-time-pairs))
|
||||
|
||||
|
||||
(define (key->source-path key)
|
||||
(car (caddr key)))
|
||||
|
||||
(require sugar/test)
|
||||
(module-test-internal
|
||||
(define ps "/users/nobody/project/source.html.pm")
|
||||
(check-equal? (key->source-path (paths->key ps)) ps))
|
||||
|
||||
(define-namespace-anchor cache-utils-module-ns)
|
||||
(define (path->hash path)
|
||||
;; new namespace forces dynamic-require to re-instantiate 'path'
|
||||
;; otherwise it gets cached in current namespace.
|
||||
(define drfs (get-directory-require-files path))
|
||||
(for-each managed-compile-zo (or drfs null))
|
||||
(define path-dir (dirname path))
|
||||
(apply hash
|
||||
(let ([doc-key (world:current-main-export)]
|
||||
[meta-key (world:current-meta-export)])
|
||||
(parameterize ([current-namespace (make-base-namespace)]
|
||||
[current-directory path-dir])
|
||||
;; I monkeyed around with using the metas submodule to pull out the metas (for speed)
|
||||
;; but in practice most files get their doc requested too.
|
||||
;; so it's just simpler to get both at once and be done with it.
|
||||
;; the savings of avoiding two cache fetches at the outset outweighs
|
||||
;; the benefit of not reloading doc when you just need metas.
|
||||
(namespace-attach-module (namespace-anchor->namespace cache-utils-module-ns) 'pollen/world) ; brings in params
|
||||
(list doc-key (dynamic-require path doc-key) meta-key (dynamic-require path meta-key))))))
|
||||
|
||||
(define (my-make-directory* dir)
|
||||
(let-values ([(base name dir?) (split-path dir)])
|
||||
(when (and (path? base) (not (directory-exists? base)))
|
||||
(my-make-directory* base))
|
||||
(unless (directory-exists? dir)
|
||||
(with-handlers ([exn:fail:filesystem:exists? void])
|
||||
(make-directory dir)))))
|
||||
|
||||
(define (make-cache-dirs path)
|
||||
(define path-dir (dirname path))
|
||||
(define cache-dir (build-path path-dir (world:current-cache-dir-name)))
|
||||
(define private-cache-dir (build-path cache-dir "private"))
|
||||
(my-make-directory* private-cache-dir) ; will also make cache-dir, if needed
|
||||
(values cache-dir private-cache-dir))
|
||||
|
||||
(define (cache-ref! key path-hash-thunk)
|
||||
(define path (key->source-path key))
|
||||
(define-values (cache-dir private-cache-dir) (make-cache-dirs path))
|
||||
(define-values (path-dir path-filename _) (split-path path))
|
||||
(define dest-file (build-path cache-dir (format "~a.rktd" path-filename)))
|
||||
(cache-file dest-file
|
||||
#:exists-ok? #t
|
||||
key
|
||||
private-cache-dir
|
||||
(λ _
|
||||
(write-to-file (path-hash-thunk) dest-file #:exists 'replace))
|
||||
#:max-cache-size (world:current-compile-cache-max-size))
|
||||
(file->value dest-file))
|
@ -0,0 +1,4 @@
|
||||
#lang racket/base
|
||||
(require "../world.rkt" sugar/file sugar/coerce sugar/test)
|
||||
(provide (all-defined-out))
|
||||
|
@ -0,0 +1,53 @@
|
||||
#lang racket/base
|
||||
(require "../world.rkt" "../file.rkt" racket/file "cache-utils.rkt" "debug.rkt" racket/path racket/place sugar/list)
|
||||
|
||||
(define (preheat-cache [starting-dir (world:current-project-root)])
|
||||
(when (or (not (path-string? starting-dir)) (not (directory-exists? starting-dir)))
|
||||
(error 'preheat-cache (format "~a is not a directory" starting-dir)))
|
||||
|
||||
(define max-places 8) ; number of parallel processes to spawn at a time
|
||||
|
||||
(define paths-that-should-be-cached (for/list ([path (in-directory starting-dir)]
|
||||
#:when (or (preproc-source? path)
|
||||
(markup-source? path)
|
||||
(markdown-source? path)
|
||||
(pagetree-source? path)))
|
||||
path))
|
||||
|
||||
;; if a file is already in the cache, no need to hit it again.
|
||||
;; this allows partially completed preheat jobs to resume.
|
||||
(define uncached-paths (filter
|
||||
(λ(path)
|
||||
;; #t = not cached; #f = already cached
|
||||
;; seems like it would be slow to load cache.rktd but it's not.
|
||||
(define-values (_ private-cache-dir) (make-cache-dirs path))
|
||||
(define cache-db-file (build-path private-cache-dir "cache.rktd"))
|
||||
(cond
|
||||
[(not (file-exists? cache-db-file)) #t]
|
||||
[else (define cache-db (file->value cache-db-file))
|
||||
(not (hash-has-key? cache-db (paths->key path)))])) paths-that-should-be-cached))
|
||||
|
||||
;; compile a path inside a place (= parallel processing)
|
||||
(define (path-into-place path)
|
||||
(message (format "caching: ~a" (find-relative-path starting-dir path)))
|
||||
(define p (place ch
|
||||
(define path (place-channel-get ch))
|
||||
(define-values (path-dir path-name _) (split-path path))
|
||||
(message (format "compiling: ~a" path-name))
|
||||
;; use #f to signal compile error. Otherwise allow errors to pass.
|
||||
(define result (with-handlers ([exn:fail? (λ _ (message (format "compile failed: ~a" path-name)) #f)])
|
||||
(path->hash path)))
|
||||
(place-channel-put ch result)))
|
||||
(place-channel-put p path)
|
||||
p)
|
||||
|
||||
;; compile the paths in groups, so they can be incrementally saved.
|
||||
;; that way, if there's a failure, the progress is preserved.
|
||||
;; but the slowest file in a group will prevent further progress.
|
||||
(for ([path-group (in-list (slice-at uncached-paths max-places))])
|
||||
(define path-places (map path-into-place path-group))
|
||||
(for ([path (in-list path-group)]
|
||||
[ppl (in-list path-places)])
|
||||
(define result (place-channel-get ppl))
|
||||
(when result ; #f is used to signal compile error
|
||||
(cache-ref! (paths->key path) (λ _ result))))))
|
@ -0,0 +1,32 @@
|
||||
#lang racket/base
|
||||
(require sugar/define
|
||||
sugar/coerce
|
||||
"../world.rkt"
|
||||
"file-utils.rkt")
|
||||
|
||||
(define+provide/contract (get-directory-require-files source-arg)
|
||||
(pathish? . -> . (or/c #f (λ(xs) (and (list? xs) (andmap complete-path? xs)))))
|
||||
(define source-path (->path source-arg))
|
||||
(define require-filenames (list world:directory-require))
|
||||
(define identity (λ(x) x))
|
||||
(define possible-requires (filter identity (map (λ(f) (find-upward-from source-path f)) require-filenames)))
|
||||
(and (pair? possible-requires) possible-requires))
|
||||
|
||||
|
||||
(define+provide/contract (require+provide-directory-require-files here-arg #:provide [provide #t])
|
||||
(pathish? . -> . list?)
|
||||
(define here-path (->path here-arg))
|
||||
(define (put-file-in-require-form file) `(file ,(path->string file)))
|
||||
(define directory-require-files (get-directory-require-files here-path))
|
||||
(if directory-require-files
|
||||
(let ([files-in-require-form (map put-file-in-require-form directory-require-files)])
|
||||
`(begin
|
||||
(require ,@files-in-require-form)
|
||||
,@(if provide
|
||||
(list `(provide (all-from-out ,@files-in-require-form)))
|
||||
null)))
|
||||
'(begin)))
|
||||
|
||||
|
||||
(define+provide (require-directory-require-files here-path)
|
||||
(require+provide-directory-require-files here-path #:provide #f))
|
Before Width: | Height: | Size: 163 B After Width: | Height: | Size: 163 B |
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 891 B After Width: | Height: | Size: 891 B |
@ -0,0 +1,21 @@
|
||||
#lang racket/base
|
||||
(provide (all-defined-out))
|
||||
|
||||
(define (split-metas tree meta-key)
|
||||
(define matches null)
|
||||
|
||||
(define (meta? x) ; meta has form (define-meta key value)
|
||||
(and (list? x) (>= (length x) 3) (eq? (car x) meta-key)))
|
||||
|
||||
(define (non-meta?/gather x)
|
||||
(or (not (meta? x))
|
||||
(and (set! matches (cons x matches)) #f)))
|
||||
|
||||
(define rest
|
||||
(let loop ([x (if (list? tree) tree (list tree))])
|
||||
(if (list? x)
|
||||
(map loop (filter non-meta?/gather x))
|
||||
x)))
|
||||
|
||||
(let ([meta-key cadr][meta-value caddr])
|
||||
(values (map meta-key matches) (map meta-value matches) rest)))
|
@ -0,0 +1,9 @@
|
||||
#lang racket/base
|
||||
(provide (all-defined-out))
|
||||
|
||||
(define (to-string x)
|
||||
(cond
|
||||
[(string? x) x]
|
||||
[(or (null? x) (void? x)) ""]
|
||||
[(or (symbol? x) (number? x) (path? x) (char? x)) (format "~a" x)]
|
||||
[else (format "~v" x)]))
|
@ -0,0 +1,3 @@
|
||||
#lang racket/base
|
||||
(provide version)
|
||||
(define version "1.0")
|
@ -0,0 +1,34 @@
|
||||
#lang racket/base
|
||||
(provide (all-defined-out))
|
||||
|
||||
(define (whitespace-base x #:nbsp-is-white? nbsp-white?)
|
||||
(define pat (pregexp (format "^[\\s~a]+$" (if nbsp-white? #\u00A0 ""))))
|
||||
(and (let loop ([x x])
|
||||
(cond
|
||||
[(string? x) (or (zero? (string-length x)) (regexp-match pat x))] ; empty string is deemed whitespace
|
||||
[(symbol? x) (loop (symbol->string x))]
|
||||
[(pair? x) (andmap loop x)]
|
||||
[(vector? x) (loop (vector->list x))]
|
||||
[else #f]))
|
||||
#t))
|
||||
|
||||
|
||||
(define (whitespace? x)
|
||||
(whitespace-base x #:nbsp-is-white? #f))
|
||||
|
||||
|
||||
(define not-whitespace? (λ(x) (not (whitespace? x))))
|
||||
|
||||
|
||||
(define (whitespace/nbsp? x)
|
||||
(whitespace-base x #:nbsp-is-white? #t))
|
||||
|
||||
|
||||
(module+ test
|
||||
(require rackunit racket/format)
|
||||
(check-true (whitespace? " "))
|
||||
(check-false (whitespace? (~a #\u00A0)))
|
||||
(check-true (whitespace/nbsp? (~a #\u00A0)))
|
||||
(check-true (whitespace/nbsp? (vector (~a #\u00A0))))
|
||||
(check-false (whitespace? (format " ~a " #\u00A0)))
|
||||
(check-true (whitespace/nbsp? (format " ~a " #\u00A0))))
|
@ -0,0 +1,8 @@
|
||||
#lang racket/base
|
||||
(require "private/main-base.rkt")
|
||||
|
||||
(define+provide-module-begin-in-mode world:mode-pagetree)
|
||||
|
||||
(module reader racket/base
|
||||
(require pollen/private/reader-base)
|
||||
(define+provide-reader-in-mode world:mode-pagetree))
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@ -0,0 +1,182 @@
|
||||
#lang scribble/manual
|
||||
|
||||
@(require scribble/eval "mb-tools.rkt" pollen/render pollen/world (for-label racket (except-in pollen #%module-begin) pollen/world sugar pollen/file))
|
||||
|
||||
@(define my-eval (make-base-eval))
|
||||
@(my-eval `(require pollen pollen/file))
|
||||
|
||||
@title[#:tag "file-types"]{File}
|
||||
|
||||
@defmodule[pollen/file]
|
||||
|
||||
A utility module that provides functions for working with Pollen source and output files. In ordinary use, you probably won't need these. But if you want to do more elaborate Pollen hacking, here they are.
|
||||
|
||||
Pollen handles six kinds of source files:
|
||||
|
||||
|
||||
@itemlist[
|
||||
@item{@bold{Preprocessor}, with file extension @ext[world:preproc-source-ext]}
|
||||
|
||||
@item{@bold{Markup}, with file extension @ext[world:markup-source-ext]}
|
||||
|
||||
@item{@bold{Markdown}, with file extension @ext[world:markdown-source-ext]}
|
||||
|
||||
@item{@bold{Null}, with file extension @ext[world:null-source-ext]}
|
||||
|
||||
@item{@bold{Scribble}, with file extension @ext[world:scribble-source-ext]}
|
||||
|
||||
@item{@bold{Pagetree}, with file extension @ext[world:pagetree-source-ext]. This is the only source type that does not produce an output file.}
|
||||
|
||||
]
|
||||
|
||||
|
||||
The functions in this module rely on file extensions specified in @racketmodname[pollen/world]. These extensions can be overridden within a project — see @secref["world-overrides"].
|
||||
|
||||
For each kind of Pollen source file, the corresponding output file name is derived by removing the extension from the name of the source file. So the preprocessor source file @filepath{default.css.pp} would become @filepath{default.css}. (See
|
||||
@secref["Saving___naming_your_source_file"] if this rings no bells.)
|
||||
|
||||
Scribble files work differently — the corresponding output file is the source file but with an @filepath{html} extension rather than @filepath{scrbl}. So @filepath["pollen.scrbl"] would become @filepath["pollen.html"].
|
||||
|
||||
For more about Pollen's file model, see @secref["File_formats"].
|
||||
|
||||
@deftogether[
|
||||
(@defproc[
|
||||
(preproc-source?
|
||||
[val any/c])
|
||||
boolean?]
|
||||
|
||||
@defproc[
|
||||
(markup-source?
|
||||
[val any/c])
|
||||
boolean?]
|
||||
|
||||
@defproc[
|
||||
(markdown-source?
|
||||
[val any/c])
|
||||
boolean?]
|
||||
|
||||
@defproc[
|
||||
(null-source?
|
||||
[val any/c])
|
||||
boolean?]
|
||||
|
||||
@defproc[
|
||||
(scribble-source?
|
||||
[val any/c])
|
||||
boolean?]
|
||||
|
||||
@defproc[
|
||||
(pagetree-source?
|
||||
[val any/c])
|
||||
boolean?]
|
||||
)]
|
||||
Test whether @racket[_val] is a path representing a source file of the specified type, based on its file extension. Does not check whether @racket[_val] exists.
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(preproc-source? "main.css.pp")
|
||||
(markup-source? "default.html.pm")
|
||||
(markdown-source? "default.html.pmd")
|
||||
(null-source? "index.html.p")
|
||||
(scribble-source? "file.scrbl")
|
||||
(pagetree-source? "index.ptree")
|
||||
]
|
||||
|
||||
|
||||
@deftogether[
|
||||
(@defproc[
|
||||
(->preproc-source-path
|
||||
[p pathish?])
|
||||
path?]
|
||||
|
||||
@defproc[
|
||||
(->markup-source-path
|
||||
[p pathish?])
|
||||
path?]
|
||||
|
||||
@defproc[
|
||||
(->markdown-source-path
|
||||
[p pathish?])
|
||||
path?]
|
||||
|
||||
@defproc[
|
||||
(->null-source-path
|
||||
[p pathish?])
|
||||
path?]
|
||||
|
||||
@defproc[
|
||||
(->scribble-source-path
|
||||
[p pathish?])
|
||||
path?]
|
||||
)]
|
||||
Convert an output path @racket[_p] into the source path of the specified type that would produce this output path. This function simply generates a corresponding source path — it does not ask whether this source path exists. (If you want a guarantee that the file exists, use @racket[get-source].)
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(define name "default.html")
|
||||
(->preproc-source-path name)
|
||||
(->markup-source-path name)
|
||||
(->markdown-source-path name)
|
||||
(->scribble-source-path name)
|
||||
(->null-source-path name)
|
||||
]
|
||||
|
||||
|
||||
@deftogether[(
|
||||
|
||||
@defproc[
|
||||
(get-source
|
||||
[p pathish?])
|
||||
(or/c #f path?)]
|
||||
|
||||
@defproc[
|
||||
(get-markup-source
|
||||
[p pathish?])
|
||||
(or/c #f path?)]
|
||||
|
||||
@defproc[
|
||||
(get-markdown-source
|
||||
[p pathish?])
|
||||
(or/c #f path?)]
|
||||
|
||||
@defproc[
|
||||
(get-preproc-source
|
||||
[p pathish?])
|
||||
(or/c #f path?)]
|
||||
|
||||
|
||||
@defproc[
|
||||
(get-null-source
|
||||
[p pathish?])
|
||||
(or/c #f path?)]
|
||||
|
||||
@defproc[
|
||||
(get-scribble-source
|
||||
[p pathish?])
|
||||
(or/c #f path?)]
|
||||
)]
|
||||
Find an existing source path that would produce the output path @racket[_p].
|
||||
|
||||
The omnibus @racket[get-source] will check source formats in this order: @racket[get-markup-source], @racket[get-markdown-source], @racket[get-preproc-source], @racket[get-null-source], and @racket[get-scribble-source].
|
||||
|
||||
The type-specific variants will, of course, only return a source file of the specified type.
|
||||
|
||||
In all cases, if there is no corresponding source, return @racket[#f].
|
||||
|
||||
|
||||
|
||||
|
||||
@defproc[
|
||||
(->output-path
|
||||
[p pathish?])
|
||||
path?]
|
||||
Convert a source path @racket[_p] into its corresponding output path. This function simply generates a path for a file — it does not ask whether the file exists.
|
||||
|
||||
If @racket[_p] has a @seclink["The_poly_output_type"]{@id[world:poly-source-ext] output type}, then @racket[->output-path] uses @racket[world:current-poly-target] as the output-path extension.
|
||||
|
||||
Otherwise, there are no type-specific variants for this function because the output path of a Pollen source file is @seclink["Saving___naming_your_source_file"]{determined by its name}.
|
||||
|
||||
@examples[#:eval my-eval
|
||||
(->output-path "main.css.pp")
|
||||
(->output-path "default.html.pm")
|
||||
(->output-path "index.html.p")
|
||||
(->output-path "file.scrbl")
|
||||
]
|
@ -0,0 +1,319 @@
|
||||
/* See the beginning of "manual.css". */
|
||||
|
||||
/* Monospace: */
|
||||
|
||||
.RktIn, .RktRdr, .RktPn, .RktMeta,
|
||||
.RktMod, .RktKw, .RktVar, .RktSym,
|
||||
.RktRes, .RktOut, .RktCmt, .RktVal,
|
||||
.RktBlk, .RktErr {
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
white-space: inherit;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* 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.15rem;
|
||||
color: black;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.inheritedlbl {
|
||||
font-family: 'Fira', sans;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.RktInBG {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
|
||||
.refcolumn .RktInBG {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.RktRdr {
|
||||
}
|
||||
|
||||
.RktPn {
|
||||
color: #843c24;
|
||||
}
|
||||
|
||||
.RktMeta {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.RktMod {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.RktOpt {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.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-weight: 500;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* for syntax links within headings */
|
||||
h2 a.RktStxLink, h3 a.RktStxLink, h4 a.RktStxLink, h5 a.RktStxLink,
|
||||
h2 a.RktValLink, h3 a.RktValLink, h4 a.RktValLink, h5 a.RktValLink,
|
||||
h2 .RktSym, h3 .RktSym, h4 .RktSym, h5 .RktSym,
|
||||
h2 .RktMod, h3 .RktMod, h4 .RktMod, h5 .RktMod,
|
||||
h2 .RktVal, h3 .RktVal, h4 .RktVal, h5 .RktVal,
|
||||
h2 .RktPn, h3 .RktPn, h4 .RktPn, h5 .RktPn {
|
||||
color: #333;
|
||||
font-size: 1.65rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.toptoclink .RktStxLink, .toclink .RktStxLink,
|
||||
.toptoclink .RktValLink, .toclink .RktValLink,
|
||||
.toptoclink .RktModLink, .toclink .RktModLink {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.tocset .RktValLink, .tocset .RktStxLink, .tocset .RktModLink {
|
||||
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: 'Source Code Pro';
|
||||
padding: 0.25rem 0.75rem 0.25rem 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
background-color: hsl(60, 29%, 94%);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
.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: 'Source Code Pro';
|
||||
font-size: 0.9rem;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
|
||||
.RpackageSpec .Smaller {
|
||||
font-weight: 300;
|
||||
font-family: 'Source Code Pro';
|
||||
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;
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function AddPartTitleOnClick(elem) {
|
||||
var mod_path = elem.getAttribute("x-source-module");
|
||||
var tag = elem.getAttribute("x-part-tag");
|
||||
if (mod_path && tag) {
|
||||
// Might not be present:
|
||||
var prefixes = elem.getAttribute("x-part-prefixes");
|
||||
|
||||
var info = document.createElement("div");
|
||||
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);
|
||||
|
||||
function add(dest, str, cn) {
|
||||
var s = document.createElement("span");
|
||||
s.className = cn;
|
||||
s.style.whiteSpace = "nowrap";
|
||||
s.appendChild(document.createTextNode(str));
|
||||
dest.appendChild(s);
|
||||
}
|
||||
/* Construct a `secref` call with suitable syntax coloring: */
|
||||
add(line1, "\xA0@", "RktRdr");
|
||||
add(line1, (is_top ? "other-doc" : "secref"), "RktSym");
|
||||
add(line1, "[", "RktPn");
|
||||
if (!is_top)
|
||||
add(line1, tag, "RktVal");
|
||||
if (is_long) {
|
||||
/* indent additional lines: */
|
||||
if (prefixes)
|
||||
add(line1x, "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0", "RktPn");
|
||||
add(line2, "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0", "RktPn");
|
||||
}
|
||||
if (prefixes) {
|
||||
add(line1x, " #:tag-prefixes ", "RktPn");
|
||||
add(line1x, "'", "RktVal");
|
||||
add(line1x, prefixes, "RktVal");
|
||||
}
|
||||
if (!is_top)
|
||||
add(line2, " #:doc ", "RktPn");
|
||||
add(line2, "'", "RktVal");
|
||||
add(line2, mod_path, "RktVal");
|
||||
add(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 header shows the explanation element: */
|
||||
elem.onclick = function () {
|
||||
if (info.style.display == "none")
|
||||
info.style.display = "block";
|
||||
else
|
||||
info.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,743 @@
|
||||
|
||||
/* 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: 'Source Code Pro', monospace;
|
||||
white-space: inherit;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.stt {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h2 .stt {
|
||||
font-size: 2.7rem;
|
||||
}
|
||||
|
||||
.toptoclink .stt {
|
||||
font-size: inherit;
|
||||
}
|
||||
.toclink .stt {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.RpackageSpec .stt {
|
||||
font-weight: 300;
|
||||
font-family: 'Source Code Pro';
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
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', serif;
|
||||
font-size: 1.18rem;
|
||||
}
|
||||
|
||||
|
||||
/* Sans-serif: */
|
||||
.version, .versionNoNav, .ssansserif {
|
||||
font-family: 'Fira', sans-serif;
|
||||
}
|
||||
.ssansserif {
|
||||
font-family: 'Fira';
|
||||
font-weight: 500;
|
||||
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';
|
||||
font-weight: 300;
|
||||
font-size: 1.6rem;
|
||||
color: #333;
|
||||
margin-top: inherit;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.25;
|
||||
-moz-font-feature-settings: 'tnum=1';
|
||||
-moz-font-feature-settings: 'tnum' 1;
|
||||
-webkit-font-feature-settings: 'tnum' 1;
|
||||
-o-font-feature-settings: 'tnum' 1;
|
||||
-ms-font-feature-settings: 'tnum' 1;
|
||||
font-feature-settings: 'tnum' 1;
|
||||
|
||||
}
|
||||
|
||||
h3, h4, h5, h6, h7, h8 {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
|
||||
|
||||
h2 { /* per-page main title */
|
||||
font-family: 'Miso';
|
||||
font-weight: bold;
|
||||
margin-top: 4rem;
|
||||
font-size: 3rem;
|
||||
line-height: 1.1;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
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';
|
||||
font-size: 0.9rem;
|
||||
border-bottom: 0px solid hsl(216, 15%, 70%);
|
||||
background-color: inherit;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navsettop {
|
||||
position: absolute;
|
||||
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 {
|
||||
height: 2rem;
|
||||
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: 1rem;
|
||||
width: 12rem;
|
||||
margin: 1rem;
|
||||
padding: 0.25rem;
|
||||
vertical-align: middle;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#search_box {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
/* 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.";
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------- */
|
||||
/* 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:1260px) {
|
||||
span.refcolumn {
|
||||
float: right;
|
||||
width: 50%;
|
||||
margin-left: 1rem;
|
||||
margin-bottom: 0.8rem;
|
||||
margin-top: 1.2rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.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:1260px) {
|
||||
.refcolumn {
|
||||
position: absolute;
|
||||
left: 66rem; right: 3em;
|
||||
margin: 0;
|
||||
float: right;
|
||||
max-width: 18rem;
|
||||
}
|
||||
}
|
||||
|
||||
.refcontent {
|
||||
font-family: 'Fira';
|
||||
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 -13em;
|
||||
}
|
||||
|
||||
.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: absolute;
|
||||
float: none;
|
||||
left: 0;
|
||||
top: 0rem;
|
||||
width: 14rem;
|
||||
padding: 7rem 0.5rem 0.5rem 0.5rem;
|
||||
background-color: hsl(216, 15%, 70%);
|
||||
margin: 0;
|
||||
|
||||
}
|
||||
|
||||
.tocset td {
|
||||
vertical-align: text-top;
|
||||
padding-bottom: 0.4rem;
|
||||
padding-left: 0.2rem;
|
||||
line-height: 1.1;
|
||||
font-family: 'Fira';
|
||||
-moz-font-feature-settings: 'tnum=1';
|
||||
-moz-font-feature-settings: 'tnum' 1;
|
||||
-webkit-font-feature-settings: 'tnum' 1;
|
||||
-o-font-feature-settings: 'tnum' 1;
|
||||
-ms-font-feature-settings: 'tnum' 1;
|
||||
font-feature-settings: 'tnum' 1;
|
||||
|
||||
}
|
||||
|
||||
.tocset td a {
|
||||
color: black;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
.tocview {
|
||||
text-align: left;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
|
||||
.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%;
|
||||
}
|
||||
|
||||
|
||||
.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';
|
||||
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;
|
||||
}
|
||||
|
||||
.boxed {
|
||||
margin: 0;
|
||||
margin-top: 2em;
|
||||
padding: 0.25em;
|
||||
padding-bottom: 0.5em;
|
||||
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, 78%, 95%) 100%);
|
||||
background: -webkit-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 78%, 95%) 100%);
|
||||
background: -o-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 78%, 95%) 100%);
|
||||
background: -ms-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 78%, 95%) 100%);
|
||||
background: linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 78%, 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;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.SubFlow {
|
||||
display: block;
|
||||
margin: 0em;
|
||||
}
|
||||
|
||||
.boxed {
|
||||
width: 100%;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.techoutside { text-decoration: none; }
|
||||
|
||||
.SAuthorListBox {
|
||||
position: static;
|
||||
float: none;
|
||||
font-family: 'Fira';
|
||||
font-weight: 300;
|
||||
font-size: 110%;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 3rem;
|
||||
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: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
border: 0;
|
||||
background-color: hsl(216, 15%, 70%);
|
||||
}
|
||||
|
||||
.searchform {
|
||||
display: inline;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.navright {
|
||||
position: absolute;
|
||||
right: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
border: 0px solid red;
|
||||
}
|
||||
|
||||
.navsetbottom {
|
||||
display: block;
|
||||
margin-top: 8rem;
|
||||
}
|
||||
|
||||
.tocset {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tocset table, .tocset tbody, .tocset tr, .tocset td {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.tocview {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tocsub .tocsubtitle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.versionbox {
|
||||
top: 4.5rem;
|
||||
left: 1rem; /* same distance as main-column */
|
||||
z-index: 11000;
|
||||
height: 2em;
|
||||
font-size: 70%;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
@ -1,6 +1,5 @@
|
||||
#lang scribble/manual
|
||||
|
||||
@(require scribble/eval (for-label pollen/pygments pollen/decode plot pollen/world pollen/tag racket/base pollen/template txexpr racket/list racket/string))
|
||||
@(require scribble/eval (for-label pollen/unstable/pygments pollen/decode plot pollen/world pollen/tag racket/base pollen/template txexpr racket/list racket/string))
|
||||
@(require "mb-tools.rkt")
|
||||
|
||||
@(define my-eval (make-base-eval))
|
Before Width: | Height: | Size: 267 KiB After Width: | Height: | Size: 267 KiB |
@ -0,0 +1,249 @@
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
.RktInBG {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.RktRdr {
|
||||
}
|
||||
|
||||
.RktPn {
|
||||
color: #843c24;
|
||||
}
|
||||
|
||||
.RktMeta {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.RktMod {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.RktOpt {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.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%;
|
||||
}
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@ -0,0 +1,170 @@
|
||||
// Common functionality for PLT documentation pages
|
||||
|
||||
// Page Parameters ------------------------------------------------------------
|
||||
|
||||
var page_query_string = location.search.substring(1);
|
||||
|
||||
var page_args =
|
||||
((function(){
|
||||
if (!page_query_string) return [];
|
||||
var args = page_query_string.split(/[&;]/);
|
||||
for (var i=0; i<args.length; i++) {
|
||||
var a = args[i];
|
||||
var p = a.indexOf('=');
|
||||
if (p >= 0) args[i] = [a.substring(0,p), a.substring(p+1)];
|
||||
else args[i] = [a, false];
|
||||
}
|
||||
return args;
|
||||
})());
|
||||
|
||||
function GetPageArg(key, def) {
|
||||
for (var i=0; i<page_args.length; i++)
|
||||
if (page_args[i][0] == key) return decodeURIComponent(page_args[i][1]);
|
||||
return def;
|
||||
}
|
||||
|
||||
function MergePageArgsIntoLink(a) {
|
||||
if (page_args.length == 0 ||
|
||||
(!a.attributes["data-pltdoc"]) || (a.attributes["data-pltdoc"].value == ""))
|
||||
return;
|
||||
a.href = MergePageArgsIntoUrl(a.href);
|
||||
}
|
||||
|
||||
function MergePageArgsIntoUrl(href) {
|
||||
var mtch = href.match(/^([^?#]*)(?:\?([^#]*))?(#.*)?$/);
|
||||
if (mtch == undefined) { // I think this never happens
|
||||
return "?" + page_query_string;
|
||||
}
|
||||
if (!mtch[2]) {
|
||||
return mtch[1] + "?" + page_query_string + (mtch[3] || "");
|
||||
}
|
||||
// need to merge here, precedence to arguments that exist in `a'
|
||||
var i, j;
|
||||
var prefix = mtch[1], str = mtch[2] || "", suffix = mtch[3] || "";
|
||||
var args = str.split(/[&;]/);
|
||||
for (i=0; i<args.length; i++) {
|
||||
j = args[i].indexOf('=');
|
||||
if (j) args[i] = args[i].substring(0,j);
|
||||
}
|
||||
var additions = "";
|
||||
for (i=0; i<page_args.length; i++) {
|
||||
var exists = false;
|
||||
for (j=0; j<args.length; j++)
|
||||
if (args[j] == page_args[i][0]) { exists = true; break; }
|
||||
if (!exists) str += "&" + page_args[i][0] + "=" + page_args[i][1];
|
||||
}
|
||||
return prefix + "?" + str + suffix;
|
||||
}
|
||||
|
||||
// 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));
|
||||
SetCookie("PLT_Root."+ver, root);
|
||||
}
|
||||
|
||||
// adding index.html works because of the above
|
||||
function GotoPLTRoot(ver, relative) {
|
||||
var u = GetCookie("PLT_Root."+ver, null);
|
||||
if (u == null) return true; // no cookie: use plain up link
|
||||
// the relative path is optional, default goes to the toplevel start page
|
||||
if (!relative) relative = "index.html";
|
||||
location = u + relative;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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.keyCode == 13) {
|
||||
var u = GetCookie("PLT_Root."+ver, null);
|
||||
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 ? "▼" : "►";
|
||||
}
|
||||
|
||||
// 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";
|
||||
});
|
@ -0,0 +1,484 @@
|
||||
|
||||
/* 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 {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* Serif: */
|
||||
.main, .refcontent, .tocview, .tocsub, .sroman, i {
|
||||
font-family: serif;
|
||||
}
|
||||
|
||||
/* Sans-serif: */
|
||||
.version, .versionNoNav, .ssansserif {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.ssansserif {
|
||||
font-size: 80%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
|
||||
p, .SIntrapara {
|
||||
display: block;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
h2 { /* per-page main title */
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h3, h4, h5, h6, h7, h8 {
|
||||
margin-top: 1.75em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.searchform {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nosearchform {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.searchbox {
|
||||
width: 16em;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#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 ";
|
||||
}
|
||||
|
||||
/* ---------------------------------------- */
|
||||
/* Margin notes */
|
||||
|
||||
.refpara, .refelem {
|
||||
position: relative;
|
||||
float: right;
|
||||
left: 2em;
|
||||
height: 0em;
|
||||
width: 13em;
|
||||
margin: 0em -13em 0em 0em;
|
||||
}
|
||||
|
||||
.refpara, .refparaleft {
|
||||
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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------- */
|
||||
/* 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;
|
||||
}
|
||||
|
||||
.SAuthorListBox {
|
||||
position: relative;
|
||||
float: right;
|
||||
left: 2em;
|
||||
top: -2.5em;
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
#lang racket/base
|
||||
(require pollen/decode pollen/misc/tutorial txexpr)
|
||||
(define (root . elements)
|
||||
(txexpr 'root null (decode-elements elements
|
||||
#:txexpr-elements-proc decode-paragraphs
|
||||
#:string-proc (compose smart-quotes smart-dashes))))
|
||||
(provide root)
|