diff --git a/file.rkt b/file.rkt index b1b1ddf..02a22cf 100644 --- a/file.rkt +++ b/file.rkt @@ -89,18 +89,20 @@ (make-source-utility-functions null) (make-source-utility-functions pagetree) (make-source-utility-functions markup) +(make-source-utility-functions markdown) (make-source-utility-functions template) (make-source-utility-functions scribble) (define/contract+provide (->source-path path) (coerce/path? . -> . (or/c #f path?)) - (ormap (λ(proc) (proc path)) (list get-markup-source get-preproc-source get-null-source get-scribble-source))) + (ormap (λ(proc) (proc path)) (list get-markup-source get-markdown-source get-preproc-source get-null-source get-scribble-source))) + (define+provide/contract (->output-path x) (coerce/path? . -> . coerce/path?) (cond - [(or (markup-source? x) (preproc-source? x) (null-source? x)) (remove-ext x)] + [(or (markup-source? x) (preproc-source? x) (null-source? x) (markdown-source? x)) (remove-ext x)] [(scribble-source? x) (add-ext (remove-ext x) 'html)] [else x])) diff --git a/main-base.rkt b/main-base.rkt index e7c13d8..0ceb0fc 100644 --- a/main-base.rkt +++ b/main-base.rkt @@ -81,7 +81,6 @@ (define metas (make-hash (map meta-element->assoc meta-elements))) (values doc-without-metas metas)) - (define doc-with-metas (let ([doc-raw (if (equal? parser-mode world:mode-markdown) (apply (compose1 (dynamic-require 'markdown 'parse-markdown) string-append) doc-raw) @@ -89,10 +88,10 @@ `(placeholder-root ,@(cons (meta 'here-path: here-path) ;; cdr strips initial linebreak, but make sure doc-raw isn't blank - (if (and (list? doc-raw) (> (length doc-raw) 0)) (cdr doc-raw) doc-raw))))) - + (if (and (list? doc-raw) (> (length doc-raw) 0) (equal? (car doc-raw) "\n")) (cdr doc-raw) doc-raw))))) + (define-values (doc-without-metas metas) (split-metas-to-hash doc-with-metas)) - + ;; set up the 'doc export (require pollen/decode) (define doc (apply (cond diff --git a/render.rkt b/render.rkt index 54213f6..c6f6ffb 100644 --- a/render.rkt +++ b/render.rkt @@ -64,7 +64,7 @@ ((pathish?) (#:force boolean?) . ->* . void?) (let ([so-path (->complete-path so-pathish)]) ; so-path = source or output path (could be either) (cond - [(ormap (λ(test) (test so-path)) (list has/is-null-source? has/is-preproc-source? has/is-markup-source? has/is-scribble-source?)) + [(ormap (λ(test) (test so-path)) (list has/is-null-source? has/is-preproc-source? has/is-markup-source? has/is-scribble-source? has/is-markdown-source?)) (let-values ([(source-path output-path) (->source+output-paths so-path)]) (render-to-file-if-needed source-path output-path #:force force))] [(pagetree-source? so-path) (render-pagetree so-path)])) @@ -75,8 +75,8 @@ (complete-path? . -> . (values complete-path? complete-path?)) ;; file-proc returns two values, but ormap only wants one (define file-proc (ormap (λ(test file-proc) (and (test source-or-output-path) file-proc)) - (list has/is-null-source? has/is-preproc-source? has/is-markup-source? has/is-scribble-source?) - (list ->null-source+output-paths ->preproc-source+output-paths ->markup-source+output-paths ->scribble-source+output-paths))) + (list has/is-null-source? has/is-preproc-source? has/is-markup-source? has/is-scribble-source? has/is-markdown-source?) + (list ->null-source+output-paths ->preproc-source+output-paths ->markup-source+output-paths ->scribble-source+output-paths ->markdown-source+output-paths))) (file-proc source-or-output-path)) @@ -119,8 +119,8 @@ (define render-proc (cond [(ormap (λ(test render-proc) (and (test source-path) render-proc)) - (list has/is-null-source? has/is-preproc-source? has/is-markup-source? has/is-scribble-source?) - (list render-null-source render-preproc-source render-markup-source render-scribble-source))] + (list has/is-null-source? has/is-preproc-source? has/is-markup-source? has/is-scribble-source? has/is-markdown-source?) + (list render-null-source render-preproc-source render-markup-or-markdown-source render-scribble-source render-markup-or-markdown-source))] [else (error (format "render: no rendering function found for ~a" source-path))])) (message (format "render: ~a" (file-name-from-path source-path))) @@ -154,7 +154,7 @@ (render-through-eval `(begin (require pollen/cache)(cached-require ,source-path ',world:main-pollen-export)))))) -(define/contract (render-markup-source source-path [maybe-template-path #f]) +(define/contract (render-markup-or-markdown-source source-path [maybe-template-path #f]) ((complete-path?) ((or/c #f complete-path?)) . ->* . bytes?) (match-define-values (source-dir _ _) (split-path source-path)) (define template-path (or maybe-template-path (get-template-for source-path))) @@ -176,7 +176,7 @@ (define/contract (templated-source? path) (complete-path? . -> . boolean?) - (and (markup-source? path))) + (or (markup-source? path) (markdown-source? path))) (define/contract+provide (get-template-for source-path) diff --git a/scribblings/acknowledgments.scrbl b/scribblings/acknowledgments.scrbl index ff27a78..3c0e9b5 100644 --- a/scribblings/acknowledgments.scrbl +++ b/scribblings/acknowledgments.scrbl @@ -6,4 +6,7 @@ One curious aspect of free software is that you can appropriate the benefits of But the best tools do more than get the job done. They create an incentive to undertake jobs you wouldn't have attempted before. Racket encouraged me to become a better programmer so I could create Pollen. Likewise, I hope that Pollen encourages you to make things you couldn't before. +Thank you to Greg Hendershott for his Markdown parser. + + MB \ No newline at end of file diff --git a/scribblings/quick.scrbl b/scribblings/quick.scrbl index d7e805d..97d5c54 100644 --- a/scribblings/quick.scrbl +++ b/scribblings/quick.scrbl @@ -2,7 +2,7 @@ @title{Quick start} - +@(define (link-tt url) (link url (tt url))) @section{Creating a source file} @@ -108,9 +108,9 @@ Project server is http://localhost:8080 (Ctrl-C to exit) Project dashboard is http://localhost:8080/index.ptree Ready to rock} -Open a web browser and point it at @tt{http://localhost:8080/index.ptree}. The top of the window will say @tt{Project root}. Below that will be a listing of the files in the directory. +Open a web browser and point it at @link-tt{http://localhost:8080/index.ptree}. The top of the window will say @tt{Project root}. Below that will be a listing of the files in the directory. -Among them will be @tt{hello.txt}, with a greyed-out @tt{.pp} extension. Click on it, and you'll be taken to @tt{http://localhost:8080/hello.txt}, where you'll see: +Among them will be @tt{hello.txt}, with a greyed-out @tt{.pp} extension. Click on it, and you'll be taken to @link-tt{http://localhost:8080/hello.txt}, where you'll see: @verbatim{ Goodbye Stranger @@ -120,75 +120,184 @@ Take the Long Way Home That's the boring part. Here's the good part. Leave the project server running. Open your source file again in DrRacket and edit it as follows: -@racketmod[pollen +@racketmod[#:file "hello.txt.pp" pollen Mean Street Panama Hear About It Later] -Go back to your web browser and reload @tt{http://localhost:8080/hello.txt}. Now you'll see this: +Go back to your web browser and reload @link-tt{http://localhost:8080/hello.txt}. Now you'll see this: @verbatim{ Mean Street Panama Hear About It Later} -The Pollen project server dyamically regenerates output files as you need them, including any time the underlying source file changes. If you like, try making some more changes to your @tt{hello.txt.pp} source file, and reloading the browser to see the updates. +Notice what happened — the Pollen project server dynamically regenerated the output file (@tt{hello.txt}) from the source file (@tt{hello.txt.pp}) after you edited the source. If you like, try making some more changes to @tt{hello.txt.pp}, and reloading the browser to see the updates in @tt{hello.txt}. @section{Intermission} -That's it for input & output. Now let's circle back and see what Pollen source files can do, other than printing plain text. +That covers input & output. Now let's circle back and look at what else you can do with Pollen (beyond the epic achievement of displaying plain text in a web browser). + +For the rest of this tutorial, I recommend keeping two windows on screen: a web-browser window pointed at your project server (the main URL is @link-tt{http://localhost:8080/index.ptree}) and the DrRacket editing window. + +@section{Pollen as a preprocessor} + +A @italic{preprocessor} is a tool for making systematic, automated changes to a source file before the main processing happens. A preprocessor can also be used to add programming logic to files that otherwise don't support it. + +For instance, HTML. In DrRacket, create a new file called @tt{margin.html.pp} in your project directory: + +@racketmod[#:file "margin.html.pp" pollen +
+5em is the inset. +] + +The ``@tt{.pp}'' file extension — which you saw before, with @tt{hello.txt.pp} — stands for ``Pollen preprocessor.'' You can use the Pollen preprocessor with any text-based file by inserting @tt{#lang pollen} as the first line, and adding the @tt{.pp} file extension. + +But for now, go to your @link["http://localhost:8080/index.ptree"]{project dashboard} and click on @link["http://localhost:8080/margin.html"]{@tt{margin.html}}. You should see a black box containing the text ``5em is the inset.'' + +Let's suppose you want to change the inset to 30%. Without a preprocessor, you'd have to search & replace each value. But with a preprocessor, you can move the inset value into a variable, and update it from that one location. So first, introduce a variable called @tt{my-inset} by using the @racket[define] command: + +@racketmod[#:file "margin.html.pp" pollen +◊define[my-inset]{30%} + +10em is the inset. + +] + +The ◊ character is called a @italic{lozenge}. In Pollen, the lozenge is a special character that marks anything Pollen should interpret as a command (rather than plain text). The whole command @tt{◊define[my-inset]{30%}} means ``create a variable called @tt{my-inset} and give it the value @tt{30%}.'' + +Then put the variable into the HTML like so, this time using the ◊ character with the variable name in the two places the value appears: + +@racketmod[#:file "margin.html.pp" pollen +◊define[my-inset]{30%} + +◊my-inset is the inset. + +] + +Now reload @link["http://localhost:8080/margin.html"]{@tt{margin.html}}. You'll see that the size of the margin has changed (because of the change to the @tt{style} attribute) and so has the text of the HTML. If you like, try editing @tt{my-inset} with different values and reloading the page. You can also try using @racket[define] to create another variable (for instance, to change the color of the box border). + +Still, this is the tiniest tip of the iceberg. The Pollen preprocessor gives you access to everything in the Racket programming language — including math functions, text manipulation, and so on. + +@section{Markdown mode} + +When used as a preprocessor, Pollen's rule is that what you write is what you get. But if you're targeting HTML, who wants to type out all those @tt{