scribble-enhanced.scrbl (8311B)
1 #lang scribble/manual 2 @require[@for-label[(except-in scribble-enhanced define-code) 3 racket/base 4 (only-in scribble/racket define-code) 5 (only-in syntax/stx stx-list?)] 6 scribble-enhanced] 7 8 @title{Scribble Enhanced} 9 @author[@author+email["Suzanne Soy" "racket@suzanne.soy"]] 10 11 @defmodule[scribble-enhanced] 12 13 @section{@racket[defform] enhancements} 14 15 @subsection{Easy rendering of quotes and syntax reader abbreviations} 16 17 The six common reader abbreviations are rendered as 18 expected, using a heuristic on source locations (so that 19 @tt{#`} renders as @tt{#`} and not @tt{quasisyntax} when the 20 source location span is exactly two characters, for 21 example). 22 23 @racketblock[ 24 @defform[#:kind "example" 25 (example-1 a 26 #'(b arg …) 27 #`(c arg …) 28 ((unsyntax 'unsyntax) (d arg …)) 29 'd 30 `e 31 ,f 32 (@#,RACKET[syntax] (e arg …)) 33 (@#,RACKET[quasisyntax] (f arg …)) 34 (@#,RACKET[unsyntax] (g arg …)) 35 (@#,RACKET[quote] d) 36 (@#,RACKET[quasiquote] e) 37 (@#,RACKET[unquote] f))]] 38 39 The above example renders as (with reader abbreviations for 40 the first six, but not for the last six): 41 42 @nested[#:style 'code-inset]{ 43 @defform[#:kind "example" 44 (example-1 a 45 #'(b arg …) 46 #`(c arg …) 47 #,(d arg …) 48 'd 49 `e 50 ,f 51 (syntax (a arg …)) 52 (quasisyntax (b arg …)) 53 (unsyntax (c arg …)) 54 (quote d) 55 (quasiquote e) 56 (unquote f))] 57 } 58 59 @subsubsection{Escaping from @racket[defform]} 60 61 Escaping from defform using @racket[UNSYNTAX] is not 62 implemented yet. 63 64 @subsection{@racket[#:result] for @racket[defform]} 65 66 @racketblock[ 67 @defform[#:kind "example" 68 (example-2 a #'([b c] ...)) 69 #:result void? 70 #:contracts ([a port?] 71 [b number?] 72 [c string?])]{ 73 Example description 74 }] 75 76 The code above renders as follows: 77 78 @nested[#:style 'code-inset]{ 79 @defform[#:kind "example" 80 (example-2 a #'([b c] ...)) 81 #:result void? 82 #:contracts ([a port?] 83 [b number?] 84 [c string?])]{ 85 Example description 86 } 87 } 88 89 @subsection{Arbitrary rewriting of code in @racket[racketblock] and similar} 90 91 @defthing[#:kind "mutable-match-lambda" 92 mutable-match-element-id-transformer]{As an example, it would be 93 possible to create a rewrite handler which turns the ⁰¹²³⁴⁵⁶⁷⁸⁹ unicode 94 superscripts at the end of identifiers into superscripts alongside the 95 base identifier. 96 97 This could be useful to typeset code using the @elem[#:style 'tt "xlist"] 98 package, which rewrites identifiers ending with a superscript to mean 99 repetition, so that @racket[(define-type three-ints (xList Integer³))] is 100 equivalent to @racket[(define-type three-ints (List Integer Integer Integer))]. 101 102 @racketblock[ 103 @(code:comment "Correctly display xyz⃰, xyzⁿ, xyz⁰, xyz¹, … xyz⁹") 104 (begin-for-syntax 105 (mutable-match-lambda-add-overriding-clause! 106 mutable-match-element-id-transformer 107 #:match-lambda 108 [(? identifier? 109 whole-id 110 (app (compose symbol->string syntax-e) 111 (pregexp #px"^(.*?)(⃰|ⁿ|[⁰¹²³⁴⁵⁶⁷⁸⁹]+)$" 112 (list whole base power)))) 113 (define/with-syntax base-id (format-id whole-id "~a" base)) 114 (define/with-syntax power-characters 115 (string-join 116 (map (match-lambda ["⃰" "*"] 117 ["ⁿ" "n"] 118 ["⁰" "0"] ["¹" "1"] ["²" "2"] ["³" "3"] ["⁴" "4"] 119 ["⁵" "5"] ["⁶" "6"] ["⁷" "7"] ["⁸" "8"] ["⁹" "9"]) 120 (map string (string->list power))))) 121 #'(elem (list (racket base-id) 122 (superscript power-characters)))]))] 123 124 Another use case would be a hack to correctly colour syntax classes from 125 syntax-parse, when used as @racket[attr:stxclass]. Here is how it would be 126 defined: 127 128 @racketblock[ 129 (begin-for-syntax 130 (mutable-match-lambda-add-overriding-clause! 131 mutable-match-element-id-transformer 132 #:match-lambda 133 [(? identifier? 134 whole-id 135 (app (compose symbol->string syntax-e) 136 (pregexp #px"^([^:]*):([^:]*)$" 137 (list whole attr cls)))) 138 (define/with-syntax attr-id (format-id whole-id "~a" attr)) 139 (define/with-syntax cls-id (format-id whole-id "~a" cls)) 140 #'(elem (list (racket attr-id) 141 (elem #:style 'tt ":") 142 (racket cls-id)))]))] 143 144 The code for these two examles would be inserted directly inside the document, 145 before any @racket[racketblock], @racket[chunk] or similar.} 146 147 @defthing[#:kind "syntax property" 148 scribble-render]{ 149 The @racket['scribble-render] syntax property can contain a function. It will 150 be called with the whole syntax object, and must return the syntax for 151 scribble code which will be used in place of that s-expression. 152 153 This feature is experimental, and may be changed in future versions. 154 155 @history[#:added "0.2" 156 #:changed "0.3" 157 @elem{Deprecated in favour of @racket[scribble-render-as].}] 158 159 @deprecated[#:what "syntax property" 160 @racket[scribble-render-as]]{ 161 Deprecated as of @racketmodname[scribble-enhanced] version 0.3, because 162 @racket['scribble-render] only supports single-line replacements. The new 163 @racket['scribble-render-as] property is more flexible.}} 164 165 @defthing[#:kind "syntax property" 166 scribble-render-as]{ 167 The @racket['scribble-render-as] syntax property can contain a function. It 168 will be called with six argumens: 169 170 @defproc[(scribble-render-as-proc 171 [self syntax?] 172 [id identifier?] 173 [typeset-expr syntax?] 174 [uncode-id identifier?] 175 [d->s-expr syntax?] 176 [stx-prop-expr syntax?]) 177 stx-list?]{} 178 179 The first argument, @racket[self], is the whole syntax object bearing the 180 @racket['scribble-render-as] property. The other arguments are the (quoted 181 syntax form of) the arguments passed to the @racket[define-code] macro which 182 generated the form currently rendering the code. The most useful argument is 183 @racket[uncode], indicating which identifier should be used in place of 184 @racket[unsyntax] to escape the current form, which will be 185 @racket[racketblock], @racket[RACKETBLOCK] or another similar form. 186 187 The function must return a syntax object which will be spliced in place of the 188 original when rendering. Note that the returned syntax object will be spliced, 189 i.e. the outer pair of parentheses removed. If the original syntax object must 190 be replaced by @racket[foo], then @racket[#'foo] must be returned. The splicing 191 operation allows several tokens to be rendered. For example, in 192 @racket[(racketblock a b c)], if @racket[b] has the 193 @racket['scribble-render-as] property, and the function returns 194 @racket[#'(x y z)], then the whole form will be rendered like 195 @racket[(racketblock a x y z c)]. 196 197 As an example, here is the @racket['scribble-render-as] procedure used by 198 @racketmodname[aful #:indirect], to render the lambda shorthand notation 199 @racket["#λ(+ % 1)"]: 200 201 @racketblock[ 202 (define (aful-scribble-render self id code typeset-code uncode d->s stx-prop) 203 (syntax-case self () 204 (code:comment "#λ(body) reads as:") 205 (code:comment "(lambda args") 206 (code:comment " (define-syntax % (make-rename-transformer #'%1))") 207 (code:comment " body)") 208 [(_ _ _ body) 209 (with-syntax ([uncode (datum->syntax uncode (syntax-e uncode) self)]) 210 (syntax/top-loc self 211 ((uncode(seclink "_lang_aful" 212 #:doc '(lib "aful/docs/aful.scrbl") 213 (tt "#λ"))) 214 body)))]))] 215 216 This feature is experimental, and may be changed in future versions. 217 218 @history[#:added "0.3"]}