Skip to content

Commit 9c5eeab

Browse files
author
Greg Hendershott
committed
Add current-entity-handler parameter.
The current-entity-handler parameter enables customizing which HTML entities are allowed in the resulting x-expressions. The default-entity-handler allows any. The scribble-entity-handler allows only those on Scribble's short list (and was the motivating use case). This is a possible alternative approach to: #48
1 parent 46accec commit 9c5eeab

2 files changed

Lines changed: 49 additions & 11 deletions

File tree

markdown/parse.rkt

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,41 @@
2222
(contract-out
2323
[read-markdown (->* () (symbol?) xexpr-element-list?)]
2424
[parse-markdown (->* ((or/c string? path?)) (symbol?) xexpr-element-list?)]
25-
[current-strict-markdown? parameter/c]))
25+
[current-strict-markdown? (parameter/c boolean?)]
26+
[current-entity-handler (parameter/c entity-handler/c)]
27+
[default-entity-handler entity-handler/c]
28+
[scribble-entity-handler entity-handler/c]))
2629

2730
(module+ test
2831
(require rackunit))
2932

3033
(define (xexpr-element-list? xs)
3134
(xexpr? `(dummy () ,@xs)))
3235

36+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
37+
;; Entity handler
38+
39+
;; Given an original xexpr and a proposed symbol entity substitution,
40+
;; an entity-handler returns which to use.
41+
(define entity-handler/c (-> xexpr/c symbol? xexpr/c))
42+
43+
;; A default entity-handler that accepts every proposed substitution.
44+
(define (default-entity-handler _ sym)
45+
sym)
46+
47+
;; An entity-handler suitable for use to produce x-expressions that
48+
;; you want to give to scribble, which expects only a limited list.
49+
(define (scribble-entity-handler orig sym)
50+
(match sym
51+
[(or 'mdash 'ndash 'ldquo 'lsquo 'rdquo 'rsquo 'larr 'rarr 'prime) sym]
52+
[_ orig]))
53+
54+
(define current-entity-handler (make-parameter default-entity-handler))
55+
56+
;; Alias for using the preceding, as we'll be using it frequently:
57+
(define (ent orig sym)
58+
((current-entity-handler) orig sym))
59+
3360
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3461
;; General purpose combinators
3562
;;
@@ -302,12 +329,12 @@
302329

303330
(define $smart-em-dash
304331
(>> (try (pdo (char #\-) (char #\-) (optional (char #\-))))
305-
(return 'mdash)))
332+
(return (ent "--" 'mdash))))
306333

307334
(define $smart-en-dash
308335
(try (pdo (char #\-)
309336
(lookAhead (pdo (many (char #\space)) $digit))
310-
(return 'ndash))))
337+
(return (ent "-" 'ndash)))))
311338

312339
(define $smart-dashes (<or> $smart-em-dash $smart-en-dash))
313340

@@ -322,12 +349,12 @@
322349
last-$str-val
323350
(for/and ([c (in-string last-$str-val)])
324351
(char-numeric? c)))
325-
(return 'prime)]
352+
(return (ent "'" 'prime))]
326353
[else (fail "")]))))
327354

328355
(define $smart-apostrophe
329356
(pdo (char #\')
330-
(return 'rsquo))) ;; could use 'apos for HTML5?
357+
(return (ent "'" 'rsquo)))) ;; could use 'apos for HTML5?
331358

332359
(define (fail-in-quote-context x)
333360
(pdo (qc <- (getState 'quote-context))
@@ -353,7 +380,7 @@
353380
(fail-just-after-str)
354381
(char #\')
355382
(lookAhead (<or> $alphaNum (char #\")))
356-
(return 'sdquo)))
383+
(return #\')))
357384

358385
(define $single-quote-end
359386
(try (>> (char #\')
@@ -363,14 +390,14 @@
363390
(try (pdo $single-quote-start
364391
(xs <- (withState (['quote-context 'single])
365392
(many1Till $inline $single-quote-end)))
366-
(return `(SPLICE lsquo ,@xs rsquo)))))
393+
(return `(SPLICE ,(ent "'" 'lsquo) ,@xs ,(ent "'" 'rsquo))))))
367394

368395
(define $double-quote-start
369396
(pdo (fail-in-quote-context 'double)
370397
(fail-just-after-str)
371398
(char #\")
372399
(lookAhead (<or> $alphaNum (char #\')))
373-
(return 'ldquo)))
400+
(return #\")))
374401

375402
(define $double-quote-end
376403
(try (>> (char #\")
@@ -380,14 +407,15 @@
380407
(try (pdo $double-quote-start
381408
(xs <- (withState (['quote-context 'double])
382409
(many1Till $inline $double-quote-end)))
383-
(return `(SPLICE ldquo ,@xs rdquo)))))
410+
(return `(SPLICE ,(ent "\"" 'ldquo) ,@xs ,(ent "\"" 'rdquo))))))
384411

385412
(define $smart-quoted (<or> $smart-quoted/single
386413
$smart-quoted/double))
387414

388415
(define $smart-ellipses
389-
(<?> (pdo (oneOfStrings "..." " . . . " ". . ." " . . .")
390-
(return 'hellip))
416+
(<?> (pdo (s <- (>>= (oneOfStrings "..." " . . . " ". . ." " . . .")
417+
(compose1 return list->string)))
418+
(return (ent s 'hellip)))
391419
"ellipsis"))
392420

393421
(define $smart-punctuation

markdown/test.rkt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,16 @@
636636
(check-md "<i>x</i><table>y</table>"
637637
'((p () (i () "x") (table () "y"))))
638638

639+
;; current-entity-handler and current-strict-markdown
640+
(let ([s @~a{"It's a nice way ... to do this"}])
641+
(parameterize ([current-strict-markdown? #f])
642+
(check-md s '((p () ldquo "It" rsquo "s a nice way " hellip " to do this" rdquo))))
643+
(parameterize ([current-strict-markdown? #t])
644+
(check-md s '((p () "\"It's a nice way ... to do this\""))))
645+
;; Use of scribble-entity-handler should avoid 'hellip in previous example.
646+
(parameterize ([current-entity-handler scribble-entity-handler])
647+
(check-md s '((p () ldquo "It" rsquo "s a nice way ... to do this" rdquo)))))
648+
639649
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
640650
;;
641651
;; Regression tests

0 commit comments

Comments
 (0)