cl-markdown-20101006-darcs/0000755000175000017500000000000011453110206013676 5ustar xachxachcl-markdown-20101006-darcs/test-results/0000755000175000017500000000000011453110206016354 5ustar xachxachcl-markdown-20101006-darcs/dev/0000755000175000017500000000000011453110206014454 5ustar xachxachcl-markdown-20101006-darcs/dev/epilogue.lisp0000644000175000017500000000020111453110206017147 0ustar xachxach;;; the last file to be loaded... (in-package #:cl-markdown) (setf *parsing-environment* (make-instance 'parsing-environment)) cl-markdown-20101006-darcs/dev/multiple-documents.lisp0000644000175000017500000001734711453110206021213 0ustar xachxach(in-package #:cl-markdown) (defun markdown-many (pairs &rest args &key format additional-extensions render-extensions &allow-other-keys) "Markdown-many processes several documents simultaneously as if it was processing one large document. Its chief purpose is to make it easy to create inter-document links. Markdown-many takes as input * `pairs` - a list of lists where each sublist contains the markdown file to be processed as `input` in its first element and the name of the file to be produced as the `output`. * `:format` - a keyword argument specifying the kind of output document to produce * `:additional-extensions` - a list of extensions that should be active both while parsing and rendering. * `:render-extensions` - a list of extensions that should be active during rendering. Here is an example: suppose document-1.md contains # Document-1 See [document-2][] for details. and document-2.md contains # Document 2 [Document-1][] provides an overview. Getting these links to work using only Markdown will require added explicit reference link information that will be tied to the file _names_. Markdown-many, on the other hand, will automatically combine the link information and processes it automatically. " (let ((main-document (make-instance 'multi-document)) (docs nil)) (setf docs (loop for datum in pairs collect (bind (((source destination &rest doc-args) datum)) (format t "~&Parsing: ~s~%" source) (list (apply #'markdown source :document-class 'child-document :parent main-document :format :none (merge-arguments args doc-args)) destination)))) ;; transfer information from docs to the parent (loop for (doc destination) in docs do (transfer-document-data main-document doc destination)) ;; render 'em (loop for (doc destination) in docs do (format t "~&Rendering: ~s" destination) (let ((*current-document* doc) (*render-active-functions* (mapcar #'canonize-command (or render-extensions (if additional-extensions `(,@additional-extensions ,@*render-active-functions*) *render-active-functions*))))) (render-to-stream doc format destination))) (setf (children main-document) (mapcar #'first docs)) (values main-document docs))) (defun merge-arguments (args-1 args-2) (let ((result args-1)) (map-window-over-elements args-2 2 2 (lambda (pair) (bind (((key value) pair) (present (getf result key))) (setf (getf result key) (if present (append (ensure-list present) (ensure-list value)) value))))) result)) #+(or) (merge-arguments '(:a (1) :b (2)) '(:c (3) :a (2))) #+(or) (defun _render-one (doc) (let ((*current-document* doc) (*render-active-functions* (mapcar #'canonize-command `(cl-markdown::docs cl-markdown::docs-index cl-markdown::today cl-markdown::now cl-markdown::glossary ,@*render-active-functions*)))) (render-to-stream doc :html #p"/tmp/one.html"))) #+(or) (untrace markdown) #+(or) (compile 'markdown-many) #+(or) (cl-markdown:markdown-many `((,(system-relative-pathname 'cl-markdown "dev/md1.md") ,(system-relative-pathname 'cl-markdown "dev/md1.html")) (,(system-relative-pathname 'cl-markdown "dev/md2.md") ,(system-relative-pathname 'cl-markdown "dev/md2.html"))) :format :html) (defun transfer-document-data (parent child destination) (transfer-link-info parent child destination) (transfer-selected-properties parent child (set-difference (collect-keys (properties child)) (list :footnote :style-sheet :style-sheets :title))) (transfer-document-metadata parent child)) (defun transfer-document-metadata (parent child) (iterate-key-value (metadata child) (lambda (key value) ; (print (list :p (item-at-1 (metadata parent) key) ; :c value)) (aif (item-at-1 (metadata parent) key) (setf (item-at-1 (metadata parent) key) (merge-entries it value)) (setf (item-at-1 (metadata parent) key) value))))) (defun transfer-selected-properties (parent child properties) (let ((*current-document* parent)) (iterate-elements properties (lambda (property) (when (item-at-1 (properties child) property) (setf (document-property property) (first (item-at-1 (properties child) property)))))))) (defun transfer-link-info (parent child destination) (let ((*current-document* parent)) (iterate-key-value (link-info child) (lambda (id info) (setf (item-at (link-info parent) id) (transfer-1-link-info info parent child destination)))))) (defgeneric transfer-1-link-info (info parent child destination)) (defmethod transfer-1-link-info ((info link-info) parent child destination) (declare (ignore parent child)) (make-instance 'link-info :id (id info) :url (if (relative-url-p (url info)) (format nil "~@[~a~]~@[.~a~]~a" (pathname-name destination) (pathname-type destination) (url info)) (url info)) :title (title info) :properties (properties info))) (defun relative-url-wrt-destination (url destination) (if (relative-url-p url) (format nil "~@[~a~]~@[.~a~]~a" (pathname-name destination) (pathname-type destination) url) url)) (defun relative-url-p (url) ;; FIXME -- look at the spec... (not (or (starts-with url "http:") (starts-with url "mailto:") (starts-with url "file:")))) (defmethod transfer-1-link-info ((info extended-link-info) parent child destination) (declare (ignore parent child destination)) (make-instance 'extended-link-info :id (id info) :kind (kind info) :contents (contents info))) ;;; ;; A slightly horrid hack that is good enough for indices but ;; completely untested (defgeneric ugly-create-from-template (thing) ) (defmethod ugly-create-from-template ((thing standard-object)) (make-instance (class-of thing))) (defgeneric merge-entries (a b) (:documentation "Returns a new container C \(of the same type as `a`\) such that C contains every *entry* in a and b. C may share structure with `a` and `b`.")) (defmethod merge-entries :around ((a t) (b t)) ; (print (list :me a b)) (call-next-method)) (defmethod merge-entries ((a null) (b t)) b) (defmethod merge-entries ((a null) (b iteratable-container-mixin)) (error "not implemented")) (defmethod merge-entries ((a null) (b key-value-iteratable-container-mixin)) (merge-using-key-value (ugly-create-from-template b) b)) (defmethod merge-entries ((a t) (b t)) (cond ((and (key-value-iteratable-p a) (key-value-iteratable-p b)) #+(or) (merge-key-value-via-iteration a b) (error "not implemented")) ((and (iteratable-p a) (iteratable-p b)) (merge-elements-via-iteration a b)) (t ;; FIXME - drop b? a))) (defmethod merge-entries ((a list) (b t)) (append a (list b))) (defmethod merge-entries ((a list) (b list)) (merge-elements-via-iteration a b)) (defmethod merge-entries ((a iteratable-container-mixin) (b iteratable-container-mixin)) (merge-elements-via-iteration a b)) (defmethod merge-entries ((a key-value-iteratable-container-mixin) (b key-value-iteratable-container-mixin)) (let ((new (ugly-create-from-template a))) (merge-using-key-value new a) (merge-using-key-value new b) new)) (defun merge-elements-via-iteration (a b) (let ((new (ugly-create-from-template a))) (iterate-elements a (lambda (elt) (insert-item new elt))) (iterate-elements b (lambda (elt) (insert-item new elt))) new)) (defun merge-using-key-value (a b) (iterate-key-value b (lambda (key value) (let ((existing (item-at a key))) (setf (item-at a key) (if existing (merge-entries existing value) value))))) a) cl-markdown-20101006-darcs/dev/spans.lisp0000644000175000017500000002341511453110206016476 0ustar xachxach(in-package #:cl-markdown) (defvar *current-span* nil) (defstruct (markdown-scanner (:conc-name scanner-)) name regex priority function) (setf (item-at-1 *spanner-parsing-environments* 'default) (make-instance 'sorted-list-container :sorter '< :key 'scanner-priority :initial-contents `(,(make-markdown-scanner :regex (create-scanner '(:sequence escaped-character)) :name 'escaped-character :priority 1 :function 'convert-escape-temporarily) ,(make-markdown-scanner :regex (create-scanner '(:sequence inline-image)) :name 'inline-image :priority 2) ,(make-markdown-scanner :regex (create-scanner '(:sequence reference-image)) :name 'reference-image :priority 3) ,(make-markdown-scanner :regex (create-scanner '(:sequence coded-reference-link)) :name 'code :priority 4) ,(make-markdown-scanner :regex (create-scanner '(:sequence inline-link)) :name 'inline-link :priority 5) ,(make-markdown-scanner :regex (create-scanner '(:sequence reference-link)) :name 'reference-link :priority 6) ,(make-markdown-scanner :regex (create-scanner '(:sequence backtick)) :name 'code :priority 7) ,(make-markdown-scanner :regex (create-scanner '(:sequence strong-em-1)) :name 'strong-em :priority 8) ,(make-markdown-scanner :regex (create-scanner '(:sequence strong-em-2)) :name 'strong-em :priority 9) ,(make-markdown-scanner :regex (create-scanner '(:sequence strong-2)) :name 'strong :priority 10) ,(make-markdown-scanner :regex (create-scanner '(:sequence strong-1)) :name 'strong :priority 11) ,(make-markdown-scanner :regex (create-scanner '(:sequence emphasis-2)) :name 'emphasis :priority 12) ,(make-markdown-scanner :regex (create-scanner '(:sequence emphasis-1)) :name 'emphasis :priority 13) ,(make-markdown-scanner :regex (create-scanner '(:sequence auto-link)) :name 'link :priority 14) ,(make-markdown-scanner :regex (create-scanner '(:sequence auto-mail)) :name 'mail :priority 15) ,(make-markdown-scanner :regex (create-scanner '(:sequence entity)) :name 'entity :priority 16) ,(make-markdown-scanner :regex (create-scanner '(:sequence html)) :name 'html :priority 7.5) ,(make-markdown-scanner :regex (create-scanner '(:sequence line-ends-with-two-spaces)) :name 'break :priority 1.8) ))) (setf (item-at-1 *spanner-parsing-environments* '(code)) (make-instance 'sorted-list-container :sorter '< :key 'scanner-priority :initial-contents `(,(make-markdown-scanner :regex (create-scanner '(:sequence html)) :name 'html :priority 1) ,(make-markdown-scanner :regex (create-scanner '(:sequence entity)) :name 'entity :priority 2)))) (defun scanners-for-chunk (chunk) (let ((it nil)) (cond ((setf it (item-at-1 *spanner-parsing-environments* (markup-class chunk))) (values it (markup-class chunk))) (t (values (item-at-1 *spanner-parsing-environments* 'default) nil))))) (defmethod handle-spans ((document abstract-document)) (iterate-elements (chunks document) (lambda (chunk) (handle-spans chunk))) document) (defmethod handle-spans ((chunk chunk)) (setf (slot-value chunk 'lines) (bind ((lines (slot-value chunk 'lines)) ((:values scanners kind) (scanners-for-chunk chunk)) (*current-span* kind)) (scan-lines-with-scanners lines scanners))) chunk) (defun scan-lines-with-scanners (lines scanners) (when (or (consp lines) (typep lines 'cl-containers:iteratable-container-mixin)) (iterate-elements scanners (lambda (scanner) (let ((name (scanner-name scanner))) (setf lines (let ((result nil)) (iterate-elements lines (lambda (line) (setf result (append result (scan-one-span line name scanner scanners))))) result)))))) lines) (defmethod scan-one-span ((line (eql nil)) scanner-name scanner scanners) (declare (ignorable scanner-name scanner scanners)) (list "")) (defmethod scan-one-span ((line cons) scanner-name scanner scanners) ;;?? what special case does this handle? (if (process-span-in-span-p scanner-name (first line)) `((,(first line) ,@(let ((*current-span* (first line))) (scan-one-span (second line) scanner-name scanner scanners)) ,@(nthcdr 2 line))) (list line))) (defmethod process-span-in-span-p ((sub-span t) (current-span t)) (values t)) (defmethod process-span-in-span-p ((sub-span (eql nil)) (current-span (eql 'html))) (values nil)) (defmethod process-span-in-span-p ((sub-span t) (current-span (eql 'html))) (values nil)) (defmethod process-span-in-span-p ((sub-span (eql 'html)) (current-span t)) (values nil)) (defmethod process-span-in-span-p ((sub-span (eql 'html)) (current-span null)) (values t)) (defmethod process-span-in-span-p ((sub-span (eql 'link)) (current-span (eql 'code))) (values nil)) (defmethod process-span-in-span-p ((sub-span (eql 'html)) (current-span (eql 'code))) (values nil)) (defmethod process-span-in-span-p ((sub-span t) (current-span (eql 'code))) (values nil)) (defmethod process-span-in-span-p ((sub-span t) (current-span (eql 'coded-reference-link))) (values nil)) (defmethod scan-one-span ((line string) scanner-name scanner scanners) #+debug (print (list :sos scanner-name *current-span* (process-span-in-span-p scanner-name *current-span*) line)) (let ((found? nil) (result nil) (last-e 0) (regex (scanner-regex scanner)) (scanner-fn (scanner-function scanner))) (when (process-span-in-span-p scanner-name *current-span*) (flet ((sub-scan (it) (let ((*current-span* scanner-name)) (scan-lines-with-scanners it scanners)))) (do-scans (s e gs ge regex line) (let ((registers (loop for s-value across gs for e-value across ge when (and (not (null s-value)) (/= s-value e-value)) collect (sub-scan (subseq line s-value e-value))))) (setf registers (process-span scanner-name registers)) (let ((converted `(,@(when (plusp s) `(,(sub-scan (subseq line last-e s)))) ,(if scanner-fn (funcall scanner-fn scanner-name registers) `(,scanner-name ,@registers))))) (setf found? t last-e e result (append result converted))))) (when found? (setf result (let ((last (sub-scan (subseq line last-e)))) (if (plusp (size last)) (append result (list last)) result))) (return-from scan-one-span (values (combine-strings result) t)))))) (values (list line) nil)) (defun combine-strings (list) (let ((result nil) (current nil)) (flet ((maybe-add (something) (when something (setf result (nconc result (list something)))))) (iterate-elements list (lambda (elt) (cond ((stringp elt) (if current (setf current (concatenate 'string current elt)) (setf current elt))) (t (maybe-add current) (maybe-add elt) (setf current nil))))) (maybe-add current) result))) #+(or) (ensure-same (combine-strings '("a" "b" 23 "c" "d")) ("ab" 23 "cd")) #+(or) (ensure-same (combine-strings '("a" "b" 23 2)) ("ab" 23 2)) #+(or) (ensure-same (combine-strings '(1 2 3 )) (1 2 3)) #+(or) (defmethod scan-one-span ((line string) scanner-name scanner scanners) (let ((found? nil) (result nil) (last-e 0) (regex (scanner-regex scanner)) (scanner-fn (scanner-function scanner)) (last-thing nil)) (when (process-span-in-span-p scanner-name *current-span*) (flet ((sub-scan (it) (let ((*current-span* scanner-name)) (scan-lines-with-scanners it scanners)))) (do-scans (s e gs ge regex line) (let ((registers (loop for s-value across gs for e-value across ge when (and (not (null s-value)) (/= s-value e-value)) collect (sub-scan (subseq line s-value e-value))))) (setf registers (process-span scanner-name registers)) (let ((converted `(,@(when (plusp s) `(,(sub-scan (subseq line last-e s)))) ,(if scanner-fn (funcall scanner-fn scanner-name registers) `(,scanner-name ,@registers))))) (print (list :c converted last-thing)) (cond ((and (stringp converted) (stringp last-thing)) (setf (first (last result)) (concatenate 'string last-thing converted))) (t (setf result (append result converted)))) (setf found? t last-e e last-thing converted)))) (when found? (return-from scan-one-span (values (let ((last (sub-scan (subseq line last-e)))) (if (plusp (size last)) (append result (list last)) result)) t)))))) (values (list line) nil)) (defun convert-escape-temporarily (scanner-name registers) (declare (ignore scanner-name)) (assert (position (aref (first registers) 0) *escape-characters*)) (format nil "~c~c~a~c~c" #\Null #\Null (position (aref (first registers) 0) *escape-characters*) #\Null #\Null)) (defmethod unconvert-escapes ((thing t)) thing) (defmethod unconvert-escapes ((string string)) (cl-ppcre:regex-replace-all '(:sequence escape-kludge) string (lambda (_ &rest registers) (declare (ignore _) (dynamic-extent registers)) ;(print registers) (let ((ch (parse-integer (first registers)))) (string (aref *escape-characters* ch)))) :simple-calls t)) (defmethod unconvert-escapes ((thing list)) (collect-elements thing :transform #'unconvert-escapes)) (defmethod unconvert-escapes ((thing chunk)) (setf (slot-value thing 'lines) (collect-elements (lines thing) :transform #'unconvert-escapes))) (defmethod unconvert-escapes ((thing abstract-document)) (iterate-elements (chunks thing) #'unconvert-escapes)) cl-markdown-20101006-darcs/dev/docs.lisp0000644000175000017500000004562311453110206016307 0ustar xachxach(in-package #:cl-markdown) (defun docs-package () (let ((property (document-property :docs-package))) (typecase property (package property) (t (let ((package (or (find-package property) (find-package (string-upcase property))))) (setf (document-property :docs-package) package)))))) (defmethod check-exportedp ((symbol cons)) ;; setf methods (check-exportedp (second symbol))) (defmethod check-exportedp ((symbol symbol)) (unless (or (eq (nth-value 1 (find-symbol (symbol-name symbol) (symbol-package symbol))) :external) (eq (nth-value 1 (find-symbol (symbol-name symbol) *package*)) :external)) (markdown-warning "Symbol ~s is not exported" symbol))) (defun ensure-documentation-holder (thing &optional (package nil)) (etypecase thing (symbol (if (and package (not (eq (symbol-package thing) package))) (intern (symbol-name thing) package) thing)) (string (if (and package (find #\space thing)) (let ((*package* package)) (read-from-string thing)) (intern thing package))) (cons (if (and (eql (first thing) 'setf) (= (length thing) 2) (typep (second thing) 'symbol)) (list 'setf (ensure-documentation-holder (second thing) package)) (error "`~a` cannot be converted into something that can hold documentation" thing))))) (defextension (docs :arguments ((name) (desired-kind)) :insertp t) (bind ((*package* (or (docs-package) *package*)) symbol) (labels ((find-docs (thing) (bind (((:values kinds nil) (let ((it nil)) (cond ((setf it (symbol-identities-with-docstring thing desired-kind)) (values it t)) (desired-kind nil) (t (values (mapcar (lambda (x) (cons x nil)) (symbol-identities thing)) nil)))))) (setf symbol thing) kinds))) (bind ((kinds (or (find-docs (ensure-documentation-holder name)) (find-docs (ensure-documentation-holder name *package*)))) (potentially-ambiguous? (> (length (symbol-identities-with-docstring symbol nil)) 1)) (kind (or (first kinds) (cons desired-kind nil))) (docs (and (cdr kind) (find-documentation symbol (form-keyword (cdr kind))))) (identity (car kind))) (ecase phase (:parse ;;?? could memoize this (where is it stored? in add-docs-item?) (cond ((> (length kinds) 1) (markdown-warning "Multiple interpretations found for ~a (~{~a~^, ~}; specify type (using ~a for now)" name kinds identity)) ((null kinds) (markdown-warning "No docstring found for ~a (package is ~s~@[, kind is ~s~])" name (package-name (docs-package)) kind)) (t (check-exportedp symbol) (add-docs-item symbol identity) ;; this is the result: t if we need to generate an anchor (documentation-needs-anchor-p name identity)))) (:render (when (first result) (anchor-documentation name identity (and (not (documentation-needs-anchor-p name nil)) potentially-ambiguous?))) (render-documentation (form-keyword identity) symbol docs) nil)))))) (defmethod render-documentation (identity symbol docs) (format *output-stream* "
" identity) (format *output-stream* "
") (format *output-stream* "
") (format *output-stream* "X") (format *output-stream* "~&") (stream-string-for-html (symbol-name symbol) *output-stream*) (format *output-stream* "") (when (thing-may-have-arguments-p symbol) (let ((arguments (mopu:function-arglist symbol))) (when arguments (format *output-stream* "~&") (display-arguments arguments :kind identity) (format *output-stream* "")))) (format *output-stream* "~&
~%") (format *output-stream* "~&~a" identity) (format *output-stream* "~&
~%") (unless (document-property :docs-signatures-only) (format *output-stream* "
") (cond (docs (markdown docs :stream *output-stream* :format *current-format* :properties '(("html" . nil) (:omit-final-paragraph . t) (:omit-initial-paragraph . t)) :document-class 'included-document)) (t (format *output-stream* "No documentation found"))) (format *output-stream* "~&
~%")) (format *output-stream* "~&
~%")) (defun documentation-needs-anchor-p (name identity) "Return true if an anchor is needed and side effects the anchors table so that it won't return true the next time it is called." (unless (documentation-anchored-p name identity) (setf (documentation-anchored-p name identity) t))) (defun anchor-documentation (name identity &optional potentially-ambiguous?) (when identity (output-anchor (docs-link-name name identity))) (unless potentially-ambiguous? (output-anchor name))) (defun docs-link-name (name identity) (format nil "~a.~a" identity name)) (defun documentation-anchors-table () (or (document-property :documentation-anchors) (setf (document-property :documentation-anchors) (make-container 'simple-associative-container :test 'equal)))) (defun documentation-anchored-p (name identity) (item-at-1 (documentation-anchors-table) (cons name identity))) (defun (setf documentation-anchored-p) (value name identity) (setf (item-at-1 (documentation-anchors-table) (cons name identity)) value)) (defun output-documentation-link (item kind text) (let ((name (html-safe-name (docs-link-name item kind)))) (format *output-stream* "~&
  • \~a
  • " name (stream-string-for-html (ensure-string text) nil)))) #| docs-index look for %items-to-index == (item-at-1 (item-at-1 (metadata *current-document*) :docs) kind) |# (defextension (docs-index :arguments ((kind-or-kinds :required) index-kind) :insertp t) (setf kind-or-kinds (ensure-list kind-or-kinds)) (unless index-kind (setf index-kind (first kind-or-kinds))) (when (eq phase :render) (bind ((items (%items-to-index kind-or-kinds))) (cond ((empty-p items) (markdown-warning (if (length-1-list-p kind-or-kinds) "There are no items of kind ~{~a~} documented." "There are no itmes of kinds~{ ~a~^ or~} documented") kind-or-kinds)) (t (output-anchor index-kind) (format *output-stream* "~&
    " index-kind) (format *output-stream* "~&
    ")))))) (defun canonize-index-kind (kind) (intern (string-downcase (ensure-string kind)) (load-time-value (find-package :cl-markdown)))) (defun %items-to-index (kinds) (let ((docs (item-at-1 (metadata *current-document*) :docs)) (kinds (mapcar #'canonize-index-kind kinds))) (sort (cond ((member 'all kinds) (let ((result nil)) (iterate-key-value docs (lambda (kind item-table) (setf result (nconc result (collect-keys item-table :transform (lambda (symbol) (cons symbol kind))))))) result)) (t (loop for kind in kinds nconc (collect-keys (item-at-1 docs kind) :transform (lambda (symbol) (cons symbol kind)))))) #'string-lessp :key #'first))) (defun add-docs-item (thing kind) (let ((kind (canonize-index-kind kind))) (add-docs-link thing kind) (bind ((docs-items (or (item-at-1 (metadata *current-document*) :docs) (setf (item-at-1 (metadata *current-document*) :docs) (make-container 'simple-associative-container)))) (kind-items (or (item-at-1 docs-items kind) (setf (item-at-1 docs-items kind) (make-container 'simple-associative-container))))) (setf (item-at-1 kind-items thing) t)))) (defun add-docs-link (thing kind) (let ((kind (canonize-index-kind kind))) (flet ((add-link (name title) (let ((anchor (html-safe-name (ensure-string name)))) (setf (item-at (link-info *current-document*) name) (make-instance 'link-info :id name :url (format nil "#~a" anchor) :title title))))) (add-link (docs-link-name thing kind) (format nil "description of ~a ~a" kind thing)) (bind ((kinds (symbol-identities-with-docstring thing))) (when (length-1-list-p kinds) (add-link (format nil "~a" thing) (format nil "description of ~a" thing))))))) (defmethod thing-may-have-arguments-p ((symbol symbol)) (and (fboundp symbol) (or (typep (symbol-function symbol) 'function) (macro-function symbol) (typep (symbol-function symbol) 'standard-generic-function)))) (defmethod thing-may-have-arguments-p ((list list)) (thing-names-setf-function-p list)) #| If there is a suppliedp variable for an optional or keyword arg, it should not be printed. If there is an initial value for an optional or keyword, it should be printed with ~S otherwise 'foo :foo "foo" all print as foo in the doc. If the initial value is a big expression, it should not print in the doc. The effect of that big expression should be explained in words. If the initial value is nil, it does not need to show in the argument line. |# #| (defun xxxfoo (triple-count index-count &key (skip-size (ag-property 'default-metaindex-skip-size)) (unique-strings (floor triple-count 3)) (average-string-size nil)) ) (let ((*output-stream* *standard-output*)) (display-arguments (mopu:function-arglist 'lift:ensure-cases) :kind 'macro)) (trace display-arguments) (untrace display-arguments) |# (defun display-arguments (arguments &key (kind nil)) ;; kind can be anything returned by symbol-identities ;; currently, we only care about macros (let ((space-entity (document-property "docs-space-entity" " ")) (first? t) (stream *output-stream*)) (dolist (argument arguments) ;; bail on &aux (when (and (symbolp argument) (string-equal (symbol-name argument) "&aux")) (return)) (unless first? (format stream "~a" space-entity)) ;; dispatch? (cond ((consp argument) (case kind (:macro (format stream "(") (display-arguments argument :kind kind) (format stream ")")) (t (cond ((eq (length argument) 3) ;; (name initform supplied) ;; ((:key name) initform supplied) ;; just recur without the supplied (display-arguments (list (butlast argument)) :kind kind)) ((eq (length argument) 2) (if (consp (car argument)) ;; ((:key name) initform) (bind ((((key name) initform) argument) (package (symbol-package name)) (new-name (intern (symbol-name key) package))) (display-arguments (list (list new-name initform)) :kind kind)) ;; (name initform) (bind (((name initform) argument) (name (stream-string-for-html (ensure-string name) nil))) (cond ((null initform) ;; just show argument (format stream "~(~a~)" name)) ((constantp initform) ;; show both (format stream "~((~a ~s)~)" name initform)) (t ;; just show name (format stream "~(~a~)" name)))))) (t ;; probably part of a macro (format stream "(") (display-arguments argument :kind kind) (format stream ")")))))) ((and (symbolp argument) (string-equal (symbol-name argument) "&" :start1 0 :start2 0 :end1 1 :end2 1)) (format stream "~(~a~)" (stream-string-for-html (symbol-name argument) nil))) (t (format stream "~(~a~)" (stream-string-for-html (symbol-name argument) nil)))) (setf first? nil)))) (defgeneric find-documentation (thing strategy) (:documentation "Return the documentation for thing using strategy. The default is to call the Common Lisp documentation method with strategy being used as the type.")) (defmethod find-documentation (thing strategy) (documentation thing (intern (symbol-name strategy) (load-time-value (find-package :common-lisp))))) (defmethod find-documentation (thing (strategy (eql :setf))) (documentation (second thing) 'setf)) (defmethod find-documentation (thing (strategy (eql :function))) (cond ((and (fboundp thing) (typep (symbol-function thing) 'standard-generic-function)) (let ((docstring (call-next-method)) (strings (loop for m in (mopu:generic-function-methods (symbol-function thing)) when (documentation m 'function) append (list (documentation m 'function))))) (format nil "~@[~a~]~:[~;~%~%~]~@[~{~a~^~%~%~}~]" docstring (and docstring strings) strings))) (t (call-next-method)))) (defparameter *symbol-identities* '((thing-names-class-p class type) (thing-names-condition-p condition nil) (thing-names-constant-p constant variable) (thing-names-function-p function nil) ;; FIXME - for now, we don't separate 'em (thing-names-generic-function-p function function) (thing-names-macro-p macro function) (thing-names-setf-function-p setf) (thing-names-slot-accessor-p function function) (thing-names-structure-p structure structure) (thing-names-type-p type type structure) (thing-names-variable-p variable nil))) (defun add-documentation-strategy (test thing strategy) (pushnew (list test thing strategy) *symbol-identities* :test #'equal :key #'first)) (defun kind-mappings (kind) "Some kinds of things have their docstrings in 'other' places. For example, macros put their docstrings under 'function. This function papers over the distinction." (case (form-keyword kind) ((:macro :generic-function) '(function)) (:class '(type)) (:condition '(type)) (:structure '(structure)) (:constant '(variable)) (:type '(type)) (t kind))) ;; FIXME - fully reconcile list of docstring with list of identities (defun symbol-identities-with-docstring (symbol &optional expected-kind) (let ((kinds (loop for kind in (ensure-list (or (and expected-kind (ensure-documentation-holder expected-kind)) (symbol-identities symbol))) for mappings = (kind-mappings kind) for docs = nil when (or (and (atom mappings) (find-documentation symbol (form-keyword mappings)) (setf docs mappings)) (and (consp mappings) (some (lambda (doc-kind) (and (find-documentation symbol (form-keyword doc-kind)) (setf docs doc-kind))) mappings))) collect (cons kind docs)))) ;; FIXME -- I've got that adhoc feeling... priority? (when (find 'macro kinds :key #'car) (setf kinds (delete 'function kinds :key #'car))) (when (find 'constant kinds :key #'car) (setf kinds (delete 'variable kinds :key #'car))) (delete-duplicates kinds :test 'eq :key #'first))) (defun symbol-identities (symbol) ;; cf. *symbol-identities* (delete-duplicates (loop for (predicate kind nil) in *symbol-identities* when (funcall predicate symbol) collect kind))) (defun thing-names-class-p (doc-holder) (and (symbolp doc-holder) (let ((class (find-class doc-holder nil))) (and class (typep class 'standard-class) (not (conditionp class)))))) ;; FIXME -- this (most likely) won't work on Lisps that don't use CLOS ;; for conditions (defun conditionp (thing) "Returns true if and only if thing is a condition" (mopu:subclassp thing 'condition)) (defun thing-names-condition-p (doc-holder) (let ((it nil)) (and (setf it (symbolp doc-holder)) (setf it (find-class doc-holder nil)) (setf it (conditionp it))))) (defun thing-names-constant-p (doc-holder) (and (symbolp doc-holder) (boundp doc-holder) (constantp doc-holder))) (defun thing-names-function-p (doc-holder) (and (symbolp doc-holder) (fboundp doc-holder) (not (macro-function doc-holder)) (typep (symbol-function doc-holder) 'function) (not (typep (symbol-function doc-holder) 'standard-generic-function)))) (defun thing-names-generic-function-p (doc-holder) (and (symbolp doc-holder) (fboundp doc-holder) (typep (symbol-function doc-holder) 'standard-generic-function) (some (lambda (m) (not (or (mopu:reader-method-p m) (mopu:writer-method-p m)))) (mopu:generic-function-methods (symbol-function doc-holder))))) (defun thing-names-macro-p (doc-holder) (and (symbolp doc-holder) (macro-function doc-holder))) (defun thing-names-setf-function-p (doc-holder) (and (consp doc-holder) (eql (first doc-holder) 'setf) (length-exactly-p doc-holder 2) (symbolp (second doc-holder)) (ignore-errors (eval `(function ,doc-holder))))) (defun thing-names-slot-accessor-p (doc-holder) (and (symbolp doc-holder) (fboundp doc-holder) (typep (symbol-function doc-holder) 'standard-generic-function) (some (lambda (m) (or (mopu:reader-method-p m) (mopu:writer-method-p m))) (mopu:generic-function-methods (symbol-function doc-holder))))) (defun thing-names-structure-p (doc-holder) (and (symbolp doc-holder) (let ((class (find-class doc-holder nil))) (and class (typep class 'structure-class))))) (defun thing-names-variable-p (doc-holder) (and (symbolp doc-holder) (boundp doc-holder) (not (constantp doc-holder)))) (defun thing-names-type-p (doc-holder) (and (symbolp doc-holder) (not (thing-names-class-p doc-holder)) (not (thing-names-condition-p doc-holder)) #+allegro (or (excl:normalize-type doc-holder :loud (lambda (&rest r) (declare (ignore r)) (return-from thing-names-type-p nil))) t))) ;;;; (defextension (links-list :arguments ((kind-or-kinds :required) index-kind)) (setf kind-or-kinds (ensure-list kind-or-kinds)) (unless index-kind (setf index-kind (first kind-or-kinds))) (when (eq phase :render) (bind ((items (%items-to-index kind-or-kinds))) (cond ((empty-p items) (markdown-warning (if (length-1-list-p kind-or-kinds) "There are no items of kind ~{~a~} documented." "There are no itmes of kinds~{ ~a~^ or~} documented") kind-or-kinds)) (t (format *output-stream* "~&(" index-kind) (loop for (item . real-kind) in items do (output-links-list-item item real-kind item))))))) (defun output-links-list-item (item kind text) (let ((name (html-safe-name (format nil "~a.~a" item kind)))) (format *output-stream* "~&(#~a ~a)" name text))) #| (defvar *foo-1* 2) (defun foo-1 () "foo-1" *foo-1*) (defun inv-foo-1 (value) "inv-foo-1" (setf *foo-1* value)) (defsetf foo-1 inv-foo-1 "defsetf foo-1") (defun (setf foo-1) (value) "(setf foo-1)" (setf *foo-1* value)) (documentation 'foo-1 'setf) |#cl-markdown-20101006-darcs/dev/plain.lisp0000644000175000017500000000420011453110206016444 0ustar xachxach(in-package #:cl-markdown) #| (markdown "`test` blue **beans**" :format :plain) (markdown "Eta * beta * data Eta" :format :plain) |# (defmethod render ((document abstract-document) (style (eql :plain)) stream) (declare (ignore stream)) (render-plain document)) (defmethod render-plain ((document abstract-document)) (bind ((current-chunk nil)) (labels ((render-block (block level markup inner?) (declare (ignore markup)) (let ((add-markup? (not (eq (first block) current-chunk)))) (cond ((or (length-1-list-p block)) (render-plain (first block))) ((not add-markup?) (render-plain (first block)) (do-it (rest block) level)) (t (setf current-chunk (and inner? (first block))) (do-it block level))))) (do-it (chunks level) (loop for rest = chunks then (rest rest) for chunk = (first rest) then (first rest) while chunk for new-level = (level chunk) when (= level new-level) do (let ((index (inner-block rest)) (inner-markup (html-inner-block-markup chunk))) (render-block (subseq rest 0 index) level inner-markup t) (setf rest (nthcdr (1- index) rest))) when (< level new-level) do (multiple-value-bind (block remaining method) (next-block rest new-level) (declare (ignore method)) (render-block block new-level (html-block-markup chunk) nil) (setf rest remaining))))) (do-it (collect-elements (chunks document)) (level document))))) (defmethod render-plain ((chunk chunk)) (bind ((paragraph? (paragraph? chunk))) (iterate-elements (lines chunk) (lambda (line) (render-plain line))) (when paragraph? (fresh-line *output-stream*)))) (defmethod render-plain ((line string)) (format *output-stream* "~a" line)) (defmethod render-plain ((chunk list)) (render-span-plain (first chunk) (rest chunk))) (defmethod render-span-plain ((code t) body) (format *output-stream* "~a" (first body))) (defmethod render-span-plain ((code (eql 'eval)) body) (render-handle-eval body)) (defmethod render-span-plain ((code (eql 'code-eval)) body) (render-handle-eval body)) cl-markdown-20101006-darcs/dev/api.lisp0000644000175000017500000000307111453110206016117 0ustar xachxach(in-package #:cl-markdown) (defgeneric reset (thing) ) (defgeneric (setf document-property) (value name)) (defgeneric render-to-stream (document style stream-specifier) ) (defgeneric main-parent (document) ) (defgeneric handle-spans (document) ) (defgeneric scan-one-span (what scanner-name scanner scanners) ) (defgeneric process-span-in-span-p (sub-span current-span) ) (defgeneric unconvert-escapes (what) ) (defgeneric render (document style stream) ) (defgeneric it-starts-with-block-level-html-p (chunk) ) (defgeneric markup-class-mergable-p (what) ) (defgeneric merge-lines-in-chunks (what) ) (defgeneric can-merge-lines-p (first second) ) (defgeneric handle-paragraph-eval-interactions (what) ) (defgeneric encode-html (what encoding-method &rest codes) ) (defgeneric markup-class-for-html (what) ) (defgeneric render-span-to-html (kind body encoding-method) ) (defgeneric generate-link-output (link-info text) ) (defgeneric add-html-header-p (document) ) (defgeneric render-plain (what) ) (defgeneric render-span-plain (kind body) ) (defgeneric process-span-for (kind command args) ) (defgeneric generate-link-output-for-kind (kind link-info text) ) (defgeneric process-span (name registers) (:documentation "Called during span processing on each match of name in the document. Registers a list of the registers captured by names regular expression. Returns a possibly new set of registers.") (:method ((name t) (registers t)) (values registers))) (defgeneric print-html-markup (markup stream) ) cl-markdown-20101006-darcs/dev/markdown.lisp0000644000175000017500000010314711453110206017175 0ustar xachxach(in-package #:cl-markdown) (defun markdown (source &key (stream *default-stream*) (format *default-format*) (additional-extensions nil) (render-extensions nil) (parse-extensions nil) (properties nil) (parent *current-document*) (document-class 'document) ) "Convert source into a markdown document object and optionally render it to stream using format. Source can be either a string or a pathname or a stream. Stream is like the stream argument in format; it can be a pathname or t \(short for *standard-output*\) or nil \(which will place the output into a string\). Format can be :html or :none. In the latter case, no output will be generated. The markdown command returns \(as multiple values\) the generated document object and any return value from the rendering \(e.g., the string produced when the stream is nil\)." ;; we chunk-source, run post-processor, handle-spans, cleanup and then render (let ((*current-document* (make-container document-class :parent parent :source source)) (*render-active-functions* (mapcar #'canonize-command (or render-extensions (if additional-extensions `(,@additional-extensions ,@*render-active-functions*) *render-active-functions*)))) (*parse-active-functions* (mapcar #'canonize-command (or parse-extensions (if additional-extensions `(,@additional-extensions ,@*parse-active-functions*) *parse-active-functions*)))) (*default-pathname-defaults* (or (and (typep source 'pathname) (containing-directory source)) *default-pathname-defaults*))) ;; pull in properties (loop for (name . value) in properties do (setf (document-property name) value)) (chunk-source *current-document* source) (iterate-elements (chunk-post-processors *parsing-environment*) (lambda (processor) (funcall processor *current-document*))) (handle-spans *current-document*) (cleanup *current-document*) (values *current-document* (render-to-stream *current-document* format stream)))) (defun containing-directory (pathspec) "Return the containing directory of the thing to which pathspac points. For example: (containing-directory \"/foo/bar/bis.temp\") \"/foo/bar/\" > (containing-directory \"/foo/bar/\") \"/foo/\" " (make-pathname :directory `(,@(butlast (pathname-directory pathspec) (if (directory-pathname-p pathspec) 1 0))) :name nil :type nil :defaults pathspec)) (defmethod render :before ((document abstract-document) style stream) (declare (ignore style)) (when (typep stream 'file-stream) (setf (destination document) (pathname stream)))) (defmethod render ((document abstract-document) (style (eql :none)) stream) (declare (ignore stream)) nil) (defmethod reset ((env parsing-environment)) (empty! (chunk-parsing-environment env)) (insert-item (chunk-parsing-environment env) (item-at-1 *chunk-parsing-environments* 'toplevel)) (setf (chunk-level env) 0 (current-strip env) "") (setf (chunk-post-processors env) (list 'handle-link-reference-titles 'handle-extended-link-references 'handle-code ; before hr and paragraphs 'handle-paragraphs ; before headers 'handle-setext-headers ; before hr 'handle-horizontal-rules ; before bullet lists, after code 'handle-bullet-lists 'handle-number-lists 'handle-bullet-paragraphs 'handle-blockquotes 'handle-atx-headers 'merge-chunks-in-document 'merge-lines-in-chunks 'canonize-document)) (empty! (line-code->stripper env)) (empty! (strippers env)) (setf (item-at-1 (line-code->stripper env) 'line-is-blockquote-p) 'blockquote-stripper (item-at-1 (line-code->stripper env) 'line-starts-with-bullet-p) 'one-tab-stripper (item-at-1 (line-code->stripper env) 'line-is-code-p) 'one-tab-stripper (item-at-1 (line-code->stripper env) 'line-starts-with-number-p) 'one-tab-stripper)) (defun null-stripper (line) (values line t)) (defun one-tab-stripper (line) (let ((indentation 0) (index 0)) (loop for ch across line while (< indentation *spaces-per-tab*) do (incf index) (cond ((char= ch #\ ) (incf indentation)) ((char= ch #\Tab) (incf indentation *spaces-per-tab*)) (t (return)))) (if (>= indentation *spaces-per-tab*) (values (subseq line index) t) (values line nil)))) ;;?? Gary King 2006-01-23: yerch, I don't like it either... (defun blockquote-stripper (line) "Strips putative initial blockquote and up to 3 spaces" (let ((count 0) (found-bq? nil)) (cond ((>= (blockquote-count line) 1) (loop repeat *spaces-per-tab* for ch across line while (and (not found-bq?) (or (char-equal ch #\ ) (and (char-equal ch #\>) (setf found-bq? t)))) do (incf count)) (cond ((not (null found-bq?)) (when (and (> (size line) (1+ count)) (char-equal (aref line count) #\ )) (incf count)) (values (subseq line count) t)) (t (values line nil)))) (t (values line nil))))) (defun blockquote-count (line) (let ((count 0)) (loop for ch across line while (or (char-equal ch #\ ) (char-equal ch #\>)) when (char-equal ch #\>) do (incf count)) count)) (defun line-indentation (line) (let ((count 0)) (or (loop for ch across line do (cond ((char-equal ch #\ ) (incf count)) ((char-equal ch #\tab) (incf count *spaces-per-tab*)) (t (return count)))) ;; empty line (values 0)))) (defun line-changes-indentation-p (line) (let ((count 0)) (loop for ch across line do (cond ((char-equal ch #\ ) (incf count)) ((char-equal ch #\tab) (incf count *spaces-per-tab*)) (t (return count)))) (unless (= *current-indentation-level* count) (setf *current-indentation-level* count) (values t)))) (defun line-starts-with-bullet-p (line) ;; a bullet and at least one space or tab after it (let* ((count 0) (bullet? (loop repeat (1- *spaces-per-tab*) for ch across line when (or (char-is-tab-or-space-p ch) (char-is-bullet-p ch)) do (incf count) when (char-is-bullet-p ch) do (return t)))) (or (and bullet? (> (length line) count) (char-is-tab-or-space-p (aref line count))) (and (not bullet?) (> (length line) (1+ count)) (char-is-bullet-p (aref line count)) (char-is-tab-or-space-p (aref line (1+ count))))))) (defun char-is-tab-or-space-p (ch) (or (char-equal ch #\ ) (char-equal ch #\Tab))) (defun char-is-bullet-p (ch) (or (char-equal ch #\*) (char-equal ch #\-) (char-equal ch #\+))) (defun line-starts-with-number-p (line) ;; at least one digit, then digits and then a period, then a space ;; FIXME -- (Why don't I use a regex?) (let* ((count 0) (number? (loop repeat (1- *spaces-per-tab*) for ch across line when (or (char-is-tab-or-space-p ch) (digit-char-p ch)) do (incf count) when (digit-char-p ch) do (return t)))) (or (and number? (> (length line) (1+ count)) (char-equal (aref line count) #\.) (whitespacep (aref line (1+ count)))) ;; this is in line-starts-with-bullet-p but looks wacked #+(or) (and (not bullet?) (> (length line) (1+ count)) (char-is-bullet-p (aref line count)) (char-is-tab-or-space-p (aref line (1+ count))))))) (defun line-is-empty-p (line) (every-element-p line #'metatilities:whitespacep)) (defun line-is-not-empty-p (line) (not (line-is-empty-p line))) (defun line-is-blockquote-p (line) (unless (line-is-code-p line) (let ((trimmed-line (string-left-trim '(#\ ) line))) (and (plusp (size trimmed-line)) (char-equal (aref trimmed-line 0) #\>))))) (defun line-is-code-p (line) (>= (line-indentation line) *spaces-per-tab*)) (defun line-could-be-header-marker-p (line) (and (or (string-starts-with line "-") (string-starts-with line "=")) (let ((first-ch (item-at line 0)) (found-ws-p nil)) (every-element-p line (lambda (ch) (cond (found-ws-p (whitespacep ch)) ((whitespacep ch) (setf found-ws-p t)) (t (char= ch first-ch)))))))) (defun line-is-link-label-p (line) (scan (load-time-value (ppcre:create-scanner '(:sequence link-label))) line)) (defun line-is-extended-link-label-p (line) (scan (load-time-value (ppcre:create-scanner '(:sequence extended-link-label))) line)) (defun line-other-p (line) (declare (ignore line)) ;; catch all (values t)) (defun horizontal-rule-char-p (char) (member char '(#\- #\* #\_) :test #'char-equal)) (defun line-is-horizontal-rule-p (line) (let ((amatch nil) (count 0) (possible-hr? nil)) (loop for char across line do (cond ((whitespacep char) ;; ignore ) ((or (and amatch (char-equal amatch char)) (and (not amatch) (horizontal-rule-char-p char))) (setf amatch char) (incf count) (when (>= count *horizontal-rule-count-threshold*) (setf possible-hr? t))) (t (setf possible-hr? nil) (return)))) (values possible-hr?))) (setf (item-at-1 *chunk-parsing-environments* 'toplevel) (make-instance 'chunk-parsing-environment :name 'toplevel :line-coders '(line-is-empty-p line-is-link-label-p line-is-extended-link-label-p line-is-code-p line-is-blockquote-p line-could-be-header-marker-p line-is-horizontal-rule-p line-starts-with-bullet-p line-starts-with-number-p line-is-not-empty-p line-other) :chunk-enders '(line-is-empty-p line-starts-with-number-p line-starts-with-bullet-p line-is-horizontal-rule-p line-is-blockquote-p line-is-link-label-p ; we'll grab title later... line-is-extended-link-label-p line-could-be-header-marker-p atx-header-p ) :chunk-starters '(line-could-be-header-marker-p atx-header-p line-is-not-empty-p ) :parser-map '((line-is-code-p code)))) (setf (item-at-1 *chunk-parsing-environments* 'line-is-code-p) (make-instance 'chunk-parsing-environment :name 'code :chunk-enders '() :chunk-starters '())) (defun maybe-strip-line (line) (bind ((env *parsing-environment*) (levels 0) (stripped? nil) ;;?? rather gross, but we don't have reverse iterators yet (strippers (reverse (collect-elements (strippers env))))) (block stripping (iterate-elements strippers (lambda (stripper) (setf (values line stripped?) (funcall stripper line)) (unless stripped? (return-from stripping)) (incf levels)))) (values line levels))) (defun chunk-source (document source) (let* ((result document) (current nil) (current-code nil) (previous-stripper nil) (level 0) (old-level level) (first? 'start-of-document) (was-blank? nil) (been-blank? nil) (just-started? nil) (*default-pathname-defaults* (or (and (typep source 'pathname) (containing-directory source)) *default-pathname-defaults*)) (line-iterator nil)) (declare (special line-iterator)) (reset *parsing-environment*) (labels ((code-line (line) (values (some-element-p (line-coders (current-chunk-parser)) (lambda (p) (funcall p line))) (some-element-p (chunk-enders (current-chunk-parser)) (lambda (p) (funcall p line))) (some-element-p (chunk-starters (current-chunk-parser)) (lambda (p) (funcall p line))))) (end-chunk-p (line code starter ender) ;; End when we have a current chunk AND either ;; the new level is bigger OR there is an ender OR ;; the new level is smaller AND the line isn't empty (declare (ignore starter)) (when (and current (or (> level old-level) (and (< level old-level) (not (line-is-empty-p line))) ender)) ;; special case for hard returns; don't end when the ;; starter is 'line-is-not-empty-p unless it is preceeded ;; by a blank line (and ;(not (eq code 'line-is-empty-p)) (or (not (eq code 'line-is-not-empty-p)) (and (eq code 'line-is-not-empty-p) was-blank?))))) (chunk-line (line) (setf (values line level) (maybe-strip-line line)) (unless (line-is-empty-p line) (loop repeat (- old-level level) while (> (size (chunk-parsing-environment *parsing-environment*)) 1) do (pop-item (chunk-parsing-environment *parsing-environment*)))) (bind (((:values code ender starter) (code-line line))) #+(or) (format t "~%~S~% L/OL: ~d/~d [~a] ~& C: ~A E: ~A S: ~A N: ~a X: ~d~@[ L: ~d~] WB: ~a, BB: ~a" line level old-level ;(size (strippers *parsing-environment*)) (collect-elements (strippers *parsing-environment*)) code ender starter (name (current-chunk-parser)) (size (chunk-parsing-environment *parsing-environment*)) (and current (size (lines current))) was-blank? been-blank?) ;; End current chunk? (when (end-chunk-p line code starter ender) #+(or) (format t " (c: ~a e: ~a) --> end~%" code ender) (setf (ended-by current) code (blank-line-after? current) (line-is-empty-p line)) (insert-item (chunks result) current) (setf previous-stripper (stripper? current)) (setf current nil)) (setf current-code code) ;; deal with embedded brackets (when (and (not (eq code 'line-is-code-p)) (or (not current) (not (eq (started-by current) 'line-is-code-p)))) (setf line (process-brackets document line line-iterator))) ;; Start new chunk? (awhen (and (not current) starter) #+(or) (format t " --> start") (let ((stripper (item-at-1 (line-code->stripper *parsing-environment*) current-code))) (setf level (+ level (if stripper 1 0)) current (make-instance 'chunk :started-by (or current-code first?) :blank-line-before? (and (not first?) was-blank?) :indentation (line-indentation line) :level level :stripper? stripper) first? nil (chunk-level *parsing-environment*) level) ;; if there is a new stripper, use it (when stripper (setf line (funcall stripper line))) (when (and (>= level old-level) stripper) (when (and previous-stripper (= level old-level)) (pop-item (strippers *parsing-environment*))) (insert-item (strippers *parsing-environment*) stripper) )) (setf just-started? t)) ;; add to current chunk (when current (if (line-is-empty-p line) (insert-item (lines current) "") (insert-item (lines current) ;; consing is fun ;; FIXME - OK, but why do I do this?! ;; A: make sure every line ends with a space. ;; Q: duh, but why?! #+(or) line (if (and (plusp (length line)) (char= (aref line (1- (length line))) #\Space)) line (concatenate 'string line " ")))) ) (loop while (> level old-level) do (insert-item (chunk-parsing-environment *parsing-environment*) (or (item-at-1 *chunk-parsing-environments* code) (item-at-1 *chunk-parsing-environments* 'toplevel))) (incf old-level)) (when (and been-blank? (not (line-is-empty-p line))) (setf old-level level) (loop while (> (size (strippers *parsing-environment*)) level) do (pop-item (strippers *parsing-environment*)))) (setf was-blank? (line-is-empty-p line)) (setf been-blank? (unless just-started? (or been-blank? was-blank?))) (setf just-started? nil) ))) (with-iterator (i source :treat-contents-as :lines :skip-empty-chunks? nil) (let ((line-iterator i)) (declare (special line-iterator)) (iterate-elements line-iterator (lambda (line) (chunk-line line)))))) ;; final processing (cond (current ;; Grab last chunk if any (setf (ended-by current) 'end-of-document) (insert-item (chunks result) current)) ((not (empty-p (chunks result))) (let ((last (last-item (chunks result)))) (setf (blank-line-after? last) t) (when (eq (ended-by last) 'line-is-empty-p) ;; maybe fix ended-by of last chunk (setf (ended-by last) 'end-of-document))))) (values result))) ;;; post processors (defun handle-horizontal-rules (document) (iterate-elements (chunks document) (lambda (chunk) (when (or (eq (started-by chunk) 'line-is-horizontal-rule-p) #+(or) ;; no -- case 12 (eq (ended-by chunk) 'line-is-horizontal-rule-p)) (empty! (lines chunk)) (setf (markup-class chunk) '(horizontal-rule)))))) (defmethod it-starts-with-block-level-html-p ((chunk chunk)) (and (not (empty-p (lines chunk))) (it-starts-with-block-level-html-p (first-element (lines chunk))))) ;; FIXME - use an r.e., gosh durn it. (defmethod it-starts-with-block-level-html-p ((line string)) (and (> (length line) 2) (char= (aref line 0) #\<) (let* ((pos-> (position #\> line :test 'char=)) (pos-space (position #\Space line :test 'char=)) (pos (and pos-> (or (and pos-space (min pos-space pos->)) pos->))) (code (and pos (subseq line 1 pos)))) (when (and code (> (length code) 0) (char= (aref code 0) #\/)) (setf code (subseq code 1))) (and code (find code *block-level-html-tags* :test 'string-equal))))) (defun handle-paragraphs (document) (let ((first? t)) (flet ((blank-before-p (chunk) (or (blank-line-before? chunk) (and ;;?? probably a hack (or (eq (started-by chunk) 'start-of-document) first?) (not (document-property :omit-initial-paragraph nil))))) (blank-after-p (chunk) (or (blank-line-after? chunk) (and ;;?? probably a hack (eq (ended-by chunk) 'end-of-document) (not (document-property :omit-final-paragraph nil)))))) (iterate-elements (chunks document) (lambda (chunk) (setf (paragraph? chunk) (and (not (it-starts-with-block-level-html-p chunk)) (not (member 'code (markup-class chunk))) (or (and (blank-before-p chunk) (blank-after-p chunk)) (and (or (blank-before-p chunk) (blank-after-p chunk)) (not (member (started-by chunk) '(line-starts-with-bullet-p line-starts-with-number-p))))))) (setf first? nil)))))) (defun handle-bullet-paragraphs (document) ;; if I have the heuristic right, a list item only gets a paragraph ;; if is following (preceeded) by another list item and there is a blank ;; line separating them. (let ((first? t)) (labels ((blank-before-p (chunk) (or (blank-line-before? chunk) (and ;;?? probably a hack (or (eq (started-by chunk) 'start-of-document) first?) (not (document-property :omit-initial-paragraph nil))))) (blank-after-p (chunk) (or (blank-line-after? chunk) (and ;;?? probably a hack (= (level chunk) 1) (eq (ended-by chunk) 'end-of-document) (not (document-property :omit-final-paragraph nil))))) (list-item-p (chunk) (member (started-by chunk) '(line-starts-with-bullet-p line-starts-with-number-p))) (handle-triple (a b c) (cond ((and (eq a b) (eq b c) (list-item-p a)) ;; all same (setf (paragraph? a) nil)) ((not (and (eq a b) (eq b c))) (when (and (list-item-p b)) ;; (print (list a b c)) (setf (paragraph? b) (or (and (list-item-p a) (= (level a) (level b)) (blank-before-p b) :bullet-before) (and (list-item-p c) (= (level b) (level c)) (blank-after-p b) :bullet-after)))))) (setf first? nil))) (map-window-over-elements (chunks document) 3 1 (lambda (triple) (bind (((a b c) triple)) (handle-triple a b c))) :duplicate-ends? t)))) (defun handle-atx-headers (document) (iterate-elements (chunks document) (lambda (chunk) (when (and (eq (started-by chunk) 'line-is-not-empty-p) (atx-header-p (first-element (lines chunk)))) (make-header chunk (atx-header-markup-class (first-element (lines chunk)))) (setf (first-element (lines chunk)) (remove-atx-header (first-element (lines chunk)))))))) (defun make-header (chunk markup-class) (push markup-class (markup-class chunk)) (setf (paragraph? chunk) nil)) (defun atx-header-p (line) (let ((first-non-hash (position-if (lambda (ch) (not (char-equal ch #\#))) line))) (and first-non-hash (< 0 first-non-hash 7)))) (defun atx-header-markup-class (line) (let ((first-non-hash (position-if (lambda (ch) (not (char-equal ch #\#))) line))) (case first-non-hash (1 'header1) (2 'header2) (3 'header3) (4 'header4) (5 'header5) (6 'header6) (t (error "Unable to determine ATX header class of ~A" line))))) (defun remove-atx-header (line) (string-trim '(#\ ) (string-trim '(#\#) line))) (defun can-merge-chunks-p (chunk1 chunk2) (and (= (level chunk1) (level chunk2)) (equal (markup-class chunk1) (markup-class chunk2)) (markup-class-mergable-p (markup-class chunk2)) (not (paragraph? chunk2)) (not (eq (ended-by chunk1) 'line-is-empty-p)))) (defmethod markup-class-mergable-p ((markup-class cons)) (every #'markup-class-mergable-p markup-class)) (defmethod markup-class-mergable-p ((markup-class symbol)) (member markup-class '(code quote))) (defun merge-chunks-in-document (document) (let ((chunks (make-iterator (chunks document))) (gatherer nil)) (cl-containers::iterate-forward chunks (lambda (chunk) (if (and gatherer (can-merge-chunks-p gatherer chunk)) (merge-chunks gatherer chunk) (setf gatherer chunk))))) (removed-ignored-chunks? document)) (defun merge-chunks (c1 c2) (iterate-elements (lines c2) (lambda (l) (insert-item (lines c1) l))) (setf (ignore? c2) t)) (defmethod merge-lines-in-chunks ((document abstract-document)) (iterate-elements (chunks document) #'merge-lines-in-chunks)) (defmethod merge-lines-in-chunks ((chunk chunk)) (unless (member 'code (markup-class chunk)) (setf (slot-value chunk 'lines) (merge-lines-in-chunks (lines chunk))))) (defmethod merge-lines-in-chunks ((lines iteratable-container-mixin)) (let ((iterator (make-iterator lines)) (gatherer "") (result nil)) (iterate-forward iterator (lambda (line) (cond ((can-merge-lines-p gatherer line) (let ((length (length gatherer))) (cond ((zerop length) (setf gatherer line)) ((char= (aref gatherer (1- length)) #\Space) (setf gatherer (concatenate 'string gatherer line))) (t (setf gatherer (concatenate 'string gatherer " " line)))))) (t (setf result (append result (list gatherer) (list line))) (setf gatherer ""))))) (when gatherer (setf result (append result (list gatherer)))) result)) (defmethod can-merge-lines-p ((line-1 string) (line-2 string)) (let ((length-1 (length line-1))) (not (and (> length-1 1) (char= (aref line-1 (- length-1 1)) #\Space) (char= (aref line-1 (- length-1 2)) #\Space))))) (defmethod can-merge-lines-p ((line-1 t) (line-2 t)) (values nil)) (defun handle-setext-headers (document) "Find headers chunks that can match up with a previous line and make it so. Also convert line into a header line. Also need to fixup setext markers that are really horizontal rules markers." (cond ((= (size (chunks document)) 1) (when (eq (started-by (first-element (chunks document))) 'line-could-be-header-marker-p) (setf (started-by (first-element (chunks document))) 'line-is-horizontal-rule-p))) ((>= (size (chunks document)) 2) (map-window-over-elements (chunks document) 2 1 (lambda (pair) (metabang-bind:bind (((p1 p2) pair)) (cond ((and (eq (started-by p2) 'line-could-be-header-marker-p) (or (empty-p (lines p1)) (eq (ended-by p1) 'line-is-empty-p))) ;; really a horizontal rule) (setf (started-by p2) 'line-is-horizontal-rule-p)) ((and (not (eq (started-by p1) 'line-is-code-p)) (not (empty-p (lines p1))) (eq (ended-by p1) 'line-could-be-header-marker-p) (eq (started-by p2) 'line-could-be-header-marker-p)) (make-header p2 (setext-header-markup-class (first-element (lines p2)))) (setf (first-element (lines p2)) (last-element (lines p1))) (delete-last (lines p1)) (when (empty-p (lines p1)) (setf (ignore? p1) t))))))))) (removed-ignored-chunks? document)) (defun removed-ignored-chunks? (document) (iterate-elements (chunks document) (lambda (chunk) (when (ignore? chunk) (delete-item (chunks document) chunk)))) document) (defun setext-header-markup-class (line) (cond ((char-equal (aref line 0) #\-) 'header2) ((char-equal (aref line 0) #\=) 'header1) (t (error "expected a setext header character and got ~A" (aref line 0))))) (defun handle-link-reference-titles (document) "Find title lines that can match up with a link reference line and make it so. Then parse the links and save them. Finally, remove those lines." ;; fixup by pulling in titles (map-window-over-elements (chunks document) 2 1 (lambda (pair) (bind (((p1 p2) pair)) (when (and (eq (started-by p1) 'line-is-link-label-p) (plusp (size (lines p2))) (line-could-be-link-reference-title-p (first-element (lines p2)))) (setf (first-element (lines p1)) (concatenate 'string (first-element (lines p1)) (first-element (lines p2))) (ended-by p1) 'line-is-link-label-p) (delete-first (lines p2)) (when (empty-p (lines p2)) (setf (ignore? p2) t)))))) ;; parse links (iterate-elements (chunks document) (lambda (chunk) (when (eq (started-by chunk) 'line-is-link-label-p) (bind (((:values nil link-info) (scan-to-strings 'link-label (first-element (lines chunk)))) (id (aref link-info 0)) (url (aref link-info 1)) (title (aref link-info 2)) (properties (aref link-info 3))) (when title ;; trim off the title delimiters (setf title (subseq title 1 (- (length title) 1)))) (setf (item-at (link-info document) id) (make-instance 'link-info :id id :url url :title title :properties properties) (ignore? chunk) t))))) ;; now remove the unneeded chunks (removed-ignored-chunks? document) document) (defun handle-extended-link-references (document) ;; find them and parse them (iterate-elements (chunks document) (lambda (chunk) (when (eq (started-by chunk) 'line-is-extended-link-label-p) (bind (((:values nil link-info) (scan-to-strings 'extended-link-label (first-element (lines chunk)))) (id (aref link-info 0)) (kind (aref link-info 1)) (contents (aref link-info 2))) (setf (item-at (link-info document) id) (make-instance 'extended-link-info :id id :kind (form-keyword kind) :contents contents) (ignore? chunk) t))))) ;; now remove the unneeded chunks (removed-ignored-chunks? document) document) (defun line-could-be-link-reference-title-p (line) "True if the first character is a quote after we skip spaces" (string-starts-with (strip-whitespace line) "\"")) (defun handle-bullet-lists (document) (iterate-elements (chunks document) (lambda (chunk) (when (eq (started-by chunk) 'line-starts-with-bullet-p) (push 'bullet (markup-class chunk)) (setf (first-element (lines chunk)) (remove-bullet (first-element (lines chunk)))))))) (defun remove-bullet (line) ;; remove (*|-|+)[.]\s* ; assume is a bullet line ;;?? possibly a hack -- but expedient! (setf line (strip-whitespace line)) (let ((pos 1) (length (size line))) (when (and (>= length 2) (char-equal (aref line 1) #\.)) (incf pos 1)) (loop while (and (> length pos) (whitespacep (aref line pos))) do (incf pos)) (subseq line pos))) (defun handle-number-lists (document) (iterate-elements (chunks document) (lambda (chunk) (when (eq (started-by chunk) 'line-starts-with-number-p) (push 'number (markup-class chunk)) (setf (first-element (lines chunk)) (remove-number (first-element (lines chunk)))))))) (defun remove-number (line) ;; remove [0-9]*\.\s* ; assume is a number line (let ((pos 0) (length (size line))) ;; digits (loop while (and (> length pos) (or (digit-char-p (aref line pos)) (char-is-tab-or-space-p (aref line pos)))) do (incf pos)) ;; required '.' (incf pos) ;; whitespace (loop while (and (> length pos) (whitespacep (aref line pos))) do (incf pos)) (subseq line pos))) (defun handle-blockquotes (document) (iterate-elements (chunks document) (lambda (chunk) (when (eq (started-by chunk) 'line-is-blockquote-p) (push 'quote (markup-class chunk)))))) (defun remove-blockquote (line) ;; removes a single level of blockquoting (let ((count 0)) ;; initial white space (loop for ch across line while (whitespacep ch) do (incf count)) ;; assume #\> (incf count) (subseq line count))) (defun handle-code (document) (iterate-elements (chunks document) (lambda (chunk) (when (eq (started-by chunk) 'line-is-code-p) (push 'code (markup-class chunk)) (setf (paragraph? chunk) nil) ;; remove last line if it is empty ;;?? is this really the right place to do this... (when (every #'whitespacep (last-item (lines chunk))) (delete-last (lines chunk)) ))))) (defun remove-indent (line) ;; removes a single level of indent (let ((count 0) (index 0)) ;; initial white space (loop for ch across line when (char-equal ch #\ ) do (incf count) when (char-equal ch #\Tab) do (incf count *spaces-per-tab*) do (incf index) while (< count *spaces-per-tab*)) (subseq line index))) ;;; canonize-document (defun canonize-document (document) (canonize-chunk-markup-class document)) (defun canonize-chunk-markup-class (document) (iterate-elements (chunks document) (lambda (chunk) (setf (markup-class chunk) (canonize-markup-class chunk))))) (defun canonize-markup-class (chunk) (setf (markup-class chunk) (sort (markup-class chunk) #'string-lessp))) (defun cleanup (document) (remove-empty-bits document) (handle-paragraph-eval-interactions document) (unconvert-escapes document) (iterate-elements (item-at-1 (properties document) :cleanup-functions) (lambda (fn) (funcall fn document)))) (defun remove-empty-bits (document) (declare (ignorable document)) ;; FIXME - if we do this, then the spacing between multiple ;; 'encoded' things (e.g., [link][] `code`) gets lost. #+(or) (iterate-elements (chunks document) (lambda (chunk) ;;?? yurk -- expediant but ugly (unless (member 'code (markup-class chunk)) (setf (slot-value chunk 'lines) (collect-elements (lines chunk) :filter (lambda (line) (not (and (stringp line) (zerop (length (string-trim +whitespace-characters+ line)))))))))))) (defmethod handle-paragraph-eval-interactions ((document abstract-document)) (iterate-elements (chunks document) #'handle-paragraph-eval-interactions)) (defmethod handle-paragraph-eval-interactions ((chunk chunk)) (unless (chunk-wants-paragraph-p chunk) #+(or) (every-element-p (lines chunk) (lambda (element) (and (consp element) (eq (first element) 'eval) (null (second (find (second element) *extensions* :key 'first)))))) (setf (paragraph? chunk) nil))) (defun chunk-wants-paragraph-p (chunk) (some-element-p (lines chunk) (lambda (line) (etypecase line (string (find-if (complement #'whitespacep) line)) (cons (or (not (eq (first line) 'eval)) (null (second (find (second line) *extensions* :key 'first))))))))) ;;; dead code #+No ;; this one merges only adjencent pairs and screws that up too b/c it merges ignored things... (defun merge-chunks-in-document (document) (map-window-over-elements (chunks document) 2 1 (lambda (pair) (metabang-bind:bind (((c1 c2) pair)) (when (can-merge-chunks-p c1 c2) (merge-chunks c1 c2))))) (removed-ignored-chunks? document)) cl-markdown-20101006-darcs/dev/reports.lisp0000644000175000017500000003012311453110206017042 0ustar xachxach(in-package #:cl-markdown) (eval-when (:compile-toplevel :load-toplevel :execute) (export '( symbols-exported-with-no-definition symbols-that-should-be-documented symbols-documented-by-document symbols-documented-by-documents symbols-not-documented-by-multi-document ))) (defmethod documents ((document document)) (list document)) (defmethod documents ((document multi-document)) (children document)) (defun build-documentation-report (source target document packages &key (format :html) excluded-symbols docs-package search-locations) (cl-markdown:markdown source :format format :stream target :additional-extensions '(unmentioned-symbols-report unused-exported-symbols-report markdown-warnings-report documented-symbols-report) :properties `((:documentation-document . ,document) (:documentation-documents . ,(documents document)) (:documentation-packages . ,(ensure-list packages)) (:documentation-excluded-symbols . ,excluded-symbols) ,@(when docs-package `((:docs-package . ,(find-package docs-package)))) (:search-locations . ,(append (list (system-relative-pathname 'cl-markdown "resources/")) (ensure-list search-locations))) (:style-sheet . "markdown-report-styles.css")))) (defun symbols-defined-by-packages (packages &key excluded-symbols) (sort (remove-duplicates (loop for package in (ensure-list packages) append (let ((package (find-package package))) (loop for s being the symbols of package when (and (eql package (symbol-package s)) (or (fboundp s) (boundp s) (find-class s nil) (get s 'prolog:functor) (eq (system:variable-information s) :special) (member s excluded-symbols) #+allegro (symbol-is-type-specifier-p s) ;; Could check deftypes if necessary. )) collect s)))) 'string< :key 'symbol-name)) (defun symbols-exported-with-no-definition (packages &key excluded-symbols) (sort (remove-duplicates (loop for package in (ensure-list packages) append (loop for s being the external-symbols of package unless (or (fboundp s) (boundp s) (find-class s nil) (get s 'prolog:functor) (eq (system:variable-information s) :special) (member s excluded-symbols) #+allegro (symbol-is-type-specifier-p s) ;; Could check deftypes if necessary. ) collect s))) 'string< :key 'symbol-name)) #+allegro (defun symbol-is-type-specifier-p (x) (or (excl:normalize-type x :loud (lambda (&rest r) (declare (ignore r)) (return-from symbol-is-type-specifier-p nil))) t)) (defun symbols-that-should-be-documented (packages &key (excluded-packages (list :common-lisp #+allegro :excl)) excluded-symbols) (let ((excluded-packages (mapcar #'find-package excluded-packages))) (sort (remove-duplicates (loop for package in (ensure-list packages) append (loop for s being the external-symbols of package unless (or (member s excluded-symbols) (member (symbol-package s) excluded-packages)) collect s))) 'string< :key 'symbol-name))) #+(or) (mapcar #'print (symbols-that-should-be-documented (list :db.agraph :db.agraph.parser :db.agraph.serializer :sparql :net.cluster))) (defun symbols-documented-by-document (document default-package) (let ((*package* (or (let ((*current-document* document)) (document-property :docs-package)) (find-package default-package))) (markdown-package (find-package :cl-markdown))) (flet ((fix-symbol (symbol) (if (eql (symbol-package symbol) markdown-package) (intern (symbol-name symbol) *package*) symbol))) (mapcar (lambda (thing) (etypecase thing (symbol (fix-symbol thing)) (cons (list (first thing) (fix-symbol (second thing)))))) (cl-containers:collect-keys (first (cl-containers:item-at-1 (cl-markdown::properties document) :documentation-anchors)) :transform #'car))))) (defun symbols-documented-by-documents (documents default-package) (remove-duplicates (loop for document in documents append (symbols-documented-by-document document default-package)))) (defun symbols-not-documented-by-multi-document (multi-doc packages default-package &key (excluded-packages (list :common-lisp #+allegro :excl)) (excluded-symbols nil)) (sort (set-difference (symbols-that-should-be-documented packages :excluded-packages excluded-packages :excluded-symbols excluded-symbols) (symbols-documented-by-documents multi-doc default-package)) #'string-lessp)) #+ignore (symbols-not-documented-by-multi-document (second *last-multi-doc*) (list :db.agraph :db.agraph.parser :db.agraph.serializer :sparql :net.cluster) :excluded-symbols (symbols-explicitly-undocumented-for-agraph)) (defextension (unused-exported-symbols-report) (when (eq phase :render) (let ((packages (document-property :documentation-packages)) (excluded (document-property :documentation-excluded-symbols)) (*package* (or (document-property :docs-package) *package*)) (os *output-stream*)) (format os "~&
    ~%") (format os "~&

    Exported Symbols with no apparent use

    ~%") (cond (packages (let ((symbols (symbols-exported-with-no-definition packages :excluded-symbols excluded))) (format os "~&
    ~%") (format os "~&

    From packages:

    ~%") (format os "~&~{~&~a~^ ~}" (mapcar #'package-name packages)) (format os "~&
    ~%") (cond ((> (length symbols) 0) (format os "~&

    ~:d Symbols

    ~%" (length symbols)) (loop for s in symbols do (format os "~&~s ~%" s))) (t (format os "~&All exported symbols are accounted for~%" (length symbols)))) (when excluded (format os "~&
    ~%") (format os "~&

    Ignoring the following ~:d symbols

    ~%" (length excluded)) (format os "~&~{~s ~^ ~%~}~%" excluded) (format os "~&
    ~%")))) (t (format os "~&

    There are no packages specified by the property :documentation-packages~%"))) (format os "~&

    ~%")))) (defextension (unmentioned-symbols-report :arguments ((packages :keyword) (excluded-symbols :keyword) (donot-display-excluded-symbols :keyword))) (when (eq phase :render) (let ((packages (or packages (document-property :documentation-packages))) (excluded (or excluded-symbols (document-property :documentation-excluded-symbols))) (documents (document-property :documentation-documents)) (*package* (or (document-property :docs-package) *package*)) (os *output-stream*)) (format os "~&
    ~%") (format os "~&

    Exported Symbols not mentioned in the documentation

    ~%") (cond (packages (let ((symbols (symbols-not-documented-by-multi-document documents packages *package* :excluded-symbols excluded))) (format os "~&
    ~%") (format os "~&

    From packages:

    ~%") (format os "~&~{~&~a ~^ ~}" (mapcar #'package-name packages)) (format os "~&
    ~%") (cond ((> (length symbols) 0) (format os "~&

    ~:d Undocumented Symbols

    ~%" (length symbols)) (loop for s in symbols do (format os "~&~s ~%" s))) (t (format os "~&All exported symbols are documented.~%"))) (when excluded (format os "~&
    ~%") (format os "~&

    Ignoring ~d symbols

    ~%" (length excluded)) (unless donot-display-excluded-symbols (format os "~&~{~s ~^ ~%~}~%" excluded)) (format os "~&
    ~%")))) (t (format os "~&

    There are no packages specified by the propoerty :documentation-packages~%"))) (format os "~&

    ~%")))) ;; FIXME -- this is wrong (it's the same as unmentioned right now) (defextension (undocumented-symbols-report :arguments ((packages :keyword) (excluded-symbols :keyword) (donot-display-excluded-symbols :keyword))) (when (eq phase :render) (let ((packages (or packages (document-property :documentation-packages))) (excluded (or excluded-symbols (document-property :documentation-excluded-symbols))) (documents (document-property :documentation-documents)) (*package* (or (document-property :docs-package) *package*)) (os *output-stream*)) (format os "~&
    ~%") (format os "~&

    Exported Symbols not mentioned in the documentation

    ~%") (cond (packages (let ((symbols (symbols-not-documented-by-multi-document documents packages *package* :excluded-symbols excluded))) (format os "~&
    ~%") (format os "~&

    From packages:

    ~%") (format os "~&~{~&~a ~^ ~}" (mapcar #'package-name packages)) (format os "~&
    ~%") (cond ((> (length symbols) 0) (format os "~&

    ~:d Undocumented Symbols

    ~%" (length symbols)) (loop for s in symbols do (format os "~&~s ~%" s))) (t (format os "~&All exported symbols are documented.~%"))) (when excluded (format os "~&
    ~%") (format os "~&

    Ignoring ~d symbols

    ~%" (length excluded)) (unless donot-display-excluded-symbols (format os "~&~{~s ~^ ~%~}~%" excluded)) (format os "~&
    ~%")))) (t (format os "~&

    There are no packages specified by the propoerty :documentation-packages~%"))) (format os "~&

    ~%")))) (defextension (documented-symbols-report) (when (eq phase :render) (let* ((documents (document-property :documentation-documents)) (*package* (or (document-property :docs-package) *package*)) (os *output-stream*) (symbols (symbols-documented-by-document documents *package*))) (format os "~&
    ~%") (format os "~&

    Documented symbols

    ~%") (cond ((> (length symbols) 0) (format os "~&

    ~:d Documented Symbols

    ~%" (length symbols)) (loop for s in symbols do (format os "~&~s ~%" s))) (t (format os "~&All exported symbols are documented.~%"))) (format os "~&
    ~%")))) (defextension (markdown-warnings-report :arguments ()) (when (eq phase :render) (bind ((os *output-stream*) (document (document-property :documentation-document)) (documents (merge-elements (warnings document) (lambda (old new) (push new old)) (lambda (new) (list new)) :key 'first :argument 'cdr :filter (lambda (pair) (typep (first pair) 'document #+(or) 'child-document)))) (warnings? nil)) (format os "~&
    ~%") (format os "~&

    Markdown Warnings

    ~%") (setf documents (sort documents #'string-lessp :key (compose 'ensure-string 'source 'first))) (loop for (document warnings) in documents do (when warnings (setf warnings? t) (format os "~&
    ~%") (format os "~&

    ~a

    ~%" (short-source (source document))) (format os "~&~%") (format os "~&
    ~%"))) (unless warnings? (format os "~%No warnings found.~%")) (format os "~&
    ~%")))) cl-markdown-20101006-darcs/dev/footnotes.lisp0000644000175000017500000001145011453110206017366 0ustar xachxach(in-package #:cl-markdown) #| To do: - allow footnotes to appear on a completely separate page - do footnotes as a popup window with mouse over - handle footnotes 'out of band' a la links Footnotes {note foo} {note "This is a note"} {note "Foo"} {note This is a note} (markdown "That is what he thought.{footnote foo} [foo]> \"This is a longer note with linefeeds, *mark-up*, and \\\"escaped\\\" quotes. I'll be wicked surprised if it works out of the box.\" ") Need to 1. get a number 2. add link where the footnote starts 3. add anchor where the footnote starts 4. add footnote text at bottom of document / separate page 5. add link back to anchor in footnote Our footnote HTML is so heavily influenced by DF that you might think we just copied it all. (markdown " Maybe people{footnote Well, at least one person} find CL-Markdown to be the bees knees, the cats pajamas and the gnats goulash. In fact, if computers could dance, you could tell that one had CL-Markdown installed on it just by watching.{footnote Not really.} {footnotes} This was generated {today} at {now}.") |# (defclass* footnote-info () ((id nil ia) (text nil ia) (reference-name nil ia) (name nil ia))) (eval-when (:load-toplevel :execute) (setf *extensions* (remove 'footnote *extensions* :key #'first)) (push (list 'footnote t) *extensions*) (setf *extensions* (remove 'footnotes *extensions* :key #'first)) (push (list 'footnotes t) *extensions*)) ;; provides an example of using result during render phase (defun footnote (phase args result) ;; {documentation text} (let ((footnotes (or (document-property :footnote) (setf (document-property :footnote) (make-instance 'vector-container))))) (cond ((eq phase :parse) (let* ((text (format nil "~{~a ~}" args))) (when text (bind ((id (size footnotes)) (fn-basename (format nil "~d-~a" id (format-date "%Y-%m-%d" (document-property :date-modified (get-universal-time))))) (fn-name (format nil "fn~a" fn-basename)) (ref-name (format nil "fnr~a" fn-basename))) (insert-item footnotes (make-instance 'footnote-info :id id :name fn-name :reference-name ref-name :text text)) (values id))))) ((eq phase :render) (let ((footnote (item-at footnotes (first result)))) (output-anchor (reference-name footnote)) (format *output-stream* "~d" (name footnote) (1+ (id footnote)))))))) (defun footnotes (phase args result) (declare (ignore args result)) (ecase phase (:parse) (:render (unless (empty-p (document-property :footnote)) (format *output-stream* "~&
    ") (format *output-stream* "~&
      ") (iterate-elements (document-property :footnote) (lambda (footnote) (format *output-stream* "~&
    1. ") (output-anchor (name footnote)) (markdown (text footnote) :stream *output-stream* :format *current-format* :properties '((:html . nil) (:omit-final-paragraph . t) (:omit-initial-paragraph . t)) :document-class 'included-document) (format *output-stream* "
    2. "))) (format *output-stream* "~&
    ~&
    "))))) ;; not yet #| (defun handle-footnote-links (document) (iterate-elements (chunks document) (lambda (chunk) (when (line-is-footnote-text-p) (bind (((values nil link-info) (scan-to-strings '(:sequence footnote-text) (first-element (lines chunk)))) (id (aref link-info 0)) (text (aref link-info 1))) (setf (item-at (link-info document) id) (make-instance 'footnote-text :id id :title text) (ignore? chunk) t))))) ;; now remove the unneeded chunks (removed-ignored-chunks? document) document) (defun line-is-footnote-text-p (line) (scan #.(ppcre:create-scanner '(:sequence footnote-text)) line)) (define-parse-tree-synonym footnote-label (:sequence :start-anchor (:greedy-repetition 0 3 :whitespace-char-class) bracketed #\> (:greedy-repetition 0 nil :whitespace-char-class) (:register (:alternation (:sequence #\" (:greedy-repetition 0 nil (:inverted-char-class #\") #\")) (:greedy-repetition 0 nil :everything))))) #+(or) (scan-to-strings (create-scanner 'footnote-label) " [a]> why are you here ok") #+(or) (scan-to-strings (create-scanner 'footnote-label) " [a]> \"why are you here? I am here because that is why. OK? ok!\"") |#cl-markdown-20101006-darcs/dev/class-defs.lisp0000644000175000017500000001143011453110206017370 0ustar xachxach(in-package #:cl-markdown) ;; someday (defclass markdown-warning () ()) (defclass* abstract-document () ((chunks (make-container 'vector-container) r) (link-info (make-container 'simple-associative-container :test #'equalp) r) (level 0 a) (markup nil a) (properties (make-container 'alist-container :test #'string-equal) r) (metadata (make-container 'alist-container :test #'string-equal) r) (bracket-references (make-container 'flexible-vector-container) r) (parent nil ir) (warnings nil a) (source nil ir) (destination nil ia) ;more or less the last place ;it was rendered (children nil ia))) (defmethod print-object ((object abstract-document) stream) (print-unreadable-object (object stream :type t :identity t) (format stream "~a" (short-source (source object))))) (defclass* document (abstract-document) ()) (defclass* child-document (document) ()) (defclass* multi-document (abstract-document) ()) (defmethod print-object ((object multi-document) stream) (print-unreadable-object (object stream :type t :identity t) (format stream "~d children" (length (children object))))) (defclass* included-document (abstract-document) ()) (defgeneric document-property (name &optional default) (:documentation "Returns the value of the property `name` of the `*current-document*` or the default if the property is not defined or there is no `*current-document*`.")) (defmethod document-property (name &optional default) (or (when *current-document* (multiple-value-bind (value found?) (item-at-1 (properties *current-document*) (form-property-name name)) (when found? (return-from document-property (first value))))) (when (and *current-document* (parent *current-document*)) (let ((*current-document* (parent *current-document*))) (document-property name default))) default)) (defmethod (setf document-property) (value name) (when *current-document* (setf (item-at-1 (properties *current-document*) (form-property-name name)) ;; so that we don't lose 'nil' (list value))) ;;?? weird since nothing happened (values value)) (defun find-link (id) (or (item-at-1 (link-info *current-document*) id) (and (parent *current-document*) (let ((*current-document* (parent *current-document*))) (find-link id))))) (defun form-property-name (name) (form-keyword (typecase name (string (intern name (load-time-value (find-package :keyword)))) (symbol (form-property-name (symbol-name name))) (t name)))) (defclass* chunk () ((lines (make-container 'vector-container) r) (blank-line-before? nil ia) (blank-line-after? nil ia) (started-by nil ia) (ended-by nil ia) (ignore? nil a) (markup-class nil ia) (indentation 0 ia) (level 0 ia) (paragraph? nil ia) (properties (make-container 'alist-container :test #'string-equal) r) (stripper? nil ia))) (defmethod initialize-instance :after ((object chunk) &key lines) (when lines (iterate-elements lines (lambda (line) (insert-item (lines object) line))))) (defmethod print-object ((chunk chunk) stream) (print-unreadable-object (chunk stream :type t) (format stream "~a~A/~A ~D lines ~A ~A" (if (paragraph? chunk) "*" "") (markup-class chunk) (level chunk) (size (lines chunk)) (started-by chunk) (ended-by chunk)))) (defclass* chunk-parsing-environment () ((name nil ir) (chunk-enders nil ia) (chunk-starters nil ia) (line-coders nil ia) (parser-map nil ia))) (defclass* parsing-environment () ((chunk-parsing-environment (make-container 'stack-container) r) (chunk-post-processors nil ia) (chunk-level 0 ia) (current-strip "" ia) (line-code->stripper (make-container 'simple-associative-container :initial-element nil #+(or) 'null-stripper) r) (strippers (make-container 'stack-container) r))) (defun current-chunk-parser () (first-item (chunk-parsing-environment *parsing-environment*))) (defclass* basic-link-info () ((id nil ia))) (defclass* link-info (basic-link-info) ((url nil ir) (title nil ia) (properties nil ia))) (defmethod initialize-instance :after ((link link-info) &key properties) (when (stringp properties) (setf (properties link) (collect-window-over-elements (string->list properties) 2 2 :transform (lambda (pair) (cons (form-keyword (first pair)) (second pair))))))) (defclass* extended-link-info (basic-link-info) ((kind nil ir) (contents nil ia))) (defmethod print-object ((object link-info) stream) (print-unreadable-object (object stream :type t :identity t) (format stream "~A -> ~A" (id object) (url object)))) cl-markdown-20101006-darcs/dev/utilities.lisp0000644000175000017500000003350411453110206017365 0ustar xachxach(in-package #:cl-markdown) (defun next-block (chunks level) (let* (;; the first chunk after the current one with a lower level (pos-level (position-if (lambda (other) (< (level other) level)) (rest chunks))) ;; the first chunk after the current one with different markup ;; and level less than or equal to (pos-markup (position-if (lambda (other) (and (<= (level other) level) (markup-class other) ;paragraphs don't count (not (samep (markup-class other) (markup-class (first chunks)))))) (rest chunks))) pos-style deeper) #+Ignore (format t "~%~d ~2,D:POS: ~A, ~A - ~A" level (length chunks) pos-level pos-markup (first chunks)) ;; remember that there will be another call to rest (cond #+(or) ((null pos-level) ;; go all the way to the end (setf deeper (subseq chunks 0) chunks nil pos-style :rest)) ((or (and pos-level pos-markup (> pos-level pos-markup)) (and pos-level (not pos-markup))) ;(format t " -- level") (setf deeper (subseq chunks 0 (1+ pos-level)) chunks (nthcdr pos-level chunks) pos-style :level)) ((or (and pos-level pos-markup (> pos-markup pos-level)) (and (not pos-level) pos-markup)) ;(format t " -- markup") (setf deeper (subseq chunks 0 (+ pos-markup 1)) chunks (nthcdr pos-markup chunks) pos-style :markup)) ((and pos-level pos-markup (= pos-level pos-markup)) ;(format t " -- level") (setf deeper (subseq chunks 0 (1+ pos-level)) chunks (nthcdr pos-level chunks) pos-style :level)) (t ;; nothing found, take the rest (setf deeper chunks chunks nil pos-style :none))) ;(format t "~{~% ~a~}" (collect-elements deeper)) (values deeper chunks pos-style))) (defmethod render-to-stream (document style stream-specifier) (with-stream-from-specifier (stream stream-specifier :output :if-exists :supersede) (let ((*current-document* document) (*current-format* style) (*output-stream* stream)) (setf (level document) 0 (markup document) nil) (render document style stream)))) (defun ensure-string (it) (typecase it (string it) (symbol (symbol-name it)) (t (format nil "~a" it)))) ;; FIXME - bad name ;; this is the parent that is a child-document or document, not, e.g., ;; an included-document (defmethod main-parent ((document included-document)) (main-parent (parent document))) (defmethod main-parent ((document abstract-document)) document) (defun root-parent (document) (or (and (parent document) (root-parent (parent document))) document)) (defun collect-links (document) (collect-key-value (link-info document) :transform (lambda (name link) (cons name (url link))) :filter (lambda (k v) (declare (ignore k)) (typep v 'link-info)))) (defun starts-with (string prefix) (let ((mismatch (mismatch prefix string))) (or (not mismatch) (= mismatch (length prefix))))) (defun markdown-warning (msg &rest args) (let* ((*print-readably* nil) (warning (apply #'format nil msg args))) (when *current-document* (push (cons (main-parent *current-document*) warning) (warnings (root-parent *current-document*)))) (fresh-line *debug-io*) (write-string warning *debug-io*) (terpri *debug-io*))) (eval-always (defun _mark-range (array start end) (loop for a from (char-code start) to (char-code end) do (setf (sbit array a) 1))) (defun _mark-one (array ch) (setf (sbit array (char-code ch)) 1))) (defparameter +first-name-characters+ (let ((array (make-array 255 :element-type 'bit :initial-element 0))) (_mark-range array #\a #\z) (_mark-range array #\A #\Z) array)) (defparameter +name-characters+ (let ((array (copy-seq +first-name-characters+))) (_mark-range array #\0 #\9) (_mark-one array #\_) (_mark-one array #\-) (_mark-one array #\.) (_mark-one array #\:) array)) (defun html-safe-name (name) ;; Copied from HTML-Encode ;;?? this is very consy ;;?? crappy name (declare (type simple-string name)) (let ((output (make-array (truncate (length name) 2/3) :element-type 'character :adjustable t :fill-pointer 0)) (first? t)) (with-output-to-string (out output) (loop for char across name for code = (char-code char) for valid = +first-name-characters+ then +name-characters+ do (cond ((and (< code 255) (= (sbit valid code) 1)) (write-char char out)) (t ;; See http://www.w3.org/TR/html4/types.html#h-6.2 ;; ID and NAME tokens must begin with a letter ([A-Za-z]) ;; and may be followed by any number of letters, ;; digits ([0-9]), hyphens ("-"), underscores ("_"), ;; colons (":"), and periods ("."). (when first? (write-char #\x out)) (format out ":~:@(~16,r~)" code))) (setf first? nil))) (coerce output 'simple-string))) (defun string->list (string &key (stop-word-p (constantly nil)) (ignore-character-p (constantly nil))) (let ((word-array (make-array (length string) :element-type 'character)) (results nil) (index 0) (in-quote-p nil)) (labels ((grab-char (ch) (setf (aref word-array index) ch) (incf index)) (add-word (word) (push word results)) (maybe-add-word () (let ((word (coerce (subseq word-array 0 index) 'string))) (unless (funcall stop-word-p word) (add-word (strip-whitespace word)))) (setf index 0))) (loop for i below (length string) for ch = (aref string i) do (cond ((char= ch #\\) (grab-char ch) (incf i) (grab-char (aref string i))) ((char= ch #\") (setf in-quote-p (not in-quote-p)) #+(or) (grab-char #\")) ((and (not in-quote-p) (> index 0) (whitespacep ch)) ;; have a word (maybe-add-word)) ((and (not in-quote-p) (funcall ignore-character-p ch)) ) (t (grab-char ch)))) (when (> index 0) (maybe-add-word)) (nreverse results)))) (defun process-brackets (document current-line line-iterator) (bind ((output (make-array 4096 :element-type 'character :adjustable t :fill-pointer 0)) (depth 0) (location 0) (buffers (bracket-references document)) (buffer-count (size buffers)) (current-buffer nil)) (with-output-to-string (out output) (block iteration-block (flet ((write-buffer-count (count) (format out " ~d" count) (setf current-buffer nil)) (add-char (ch) (if current-buffer (vector-push-extend ch current-buffer) (write-char ch out))) (start-buffer () (setf current-buffer (make-array 4096 :element-type 'character :adjustable t :fill-pointer 0)))) (flet ((process-brackets-in-line (line) ;;(print (list :line line)) (with-iterator (iterator line :treat-contents-as :characters :skip-empty-chunks? nil) (iterate-elements iterator (lambda (ch) ;;(print (list ch (not (null current-buffer)))) (incf location) (cond ((char= ch #\\) (unless (move-forward-p iterator) (error "Invalid escape at char ~d" location)) (add-char ch) (add-char (next-element iterator))) ((and (= depth 1) (not current-buffer) (whitespacep ch)) ;; finished reading the command name ;; (don't write the ws) (start-buffer)) ((char= ch #\{) (incf depth) (add-char ch)) ((char= ch #\}) (decf depth) (cond ((= depth 0) (insert-item buffers (coerce current-buffer 'simple-string)) (write-buffer-count buffer-count)) ((< depth 0) ;; FIXME -- an error (setf depth 0))) (add-char ch)) (t (add-char ch)))))) ;; if no brackets to process at the end of a line, bail (when (= depth 0) (return-from iteration-block nil)) (if (and (= depth 1) (not current-buffer)) (start-buffer) (add-char #\Newline)))) (process-brackets-in-line current-line) (move-forward line-iterator) (iterate-elements line-iterator (lambda (line) (process-brackets-in-line line)))))) (coerce output 'simple-string)))) #+ignore (let ((li (make-iterator "a b c d e f" :treat-contents-as :lines))) (iterate-elements li (lambda (x) (iterate-elements li (lambda (y) (let ((ci (make-iterator y :treat-contents-as :characters :skip-empty-chunks? nil))) (iterate-elements ci #'print))))))) (defun short-source (source) (typecase source (pathname source) (string (format nil "~a~@[...~]" (substitute-if #\Space (lambda (ch) (or (char= ch #\newline) (char= ch #\linefeed))) (subseq source 0 (min 50 (length source)))) (> (length source) 50))) (t (format nil "Something of type ~s" (type-of source))))) (defun could-be-html-tag-p (string start) ;; assumes that start == the index _after_ the #\< (let ((state :start)) (loop for index from start below (length string) for ch = (schar string index) do (ecase state (:start (when (whitespacep ch) (return nil)) (setf state :running)) (:running (case ch (#\' (setf state :single-quote)) (#\" (setf state :double-quote)) (#\> (return index)))) (:single-quote (case ch (#\' (setf state :running)))) (:double-quote (case ch (#\" (setf state :running)))))))) ;; ;; ^ (defun stream-string-for-html (string stream) (declare (simple-string string)) (let ((next-index 1) (last-index nil)) (with-output (out stream) (loop for char across string do (cond ((char= char #\&) (write-string "&" out)) ((char= char #\<) (setf last-index (could-be-html-tag-p string next-index)) (if last-index (write-char char out) (write-string "<" out))) ((and (null last-index) (char= char #\>)) (write-string ">" out)) (t (write-char char out))) (when (and last-index (> next-index last-index)) (setf last-index nil)) (incf next-index))))) #+(or) (stream-string-for-html "hello a > b and b < a " nil) (defun encode-string-for-html (string) (stream-string-for-html string nil)) ;; Copied from HTML-Encode ;;?? this is very consy ;;?? crappy name (defun encode-pre (string) (declare (simple-string string)) (let ((output (make-array (truncate (length string) 2/3) :element-type 'character :adjustable t :fill-pointer 0))) (with-output-to-string (out output) (loop for char across string do (case char ((#\&) (write-string "&" out)) ((#\<) (write-string "<" out)) ((#\>) (write-string ">" out)) (t (write-char char out))))) (coerce output 'simple-string))) ;; Copied from HTML-Encode ;;?? this is very consy ;;?? crappy name ;;?? this is really bugging me -- what gross code and it's repeated FOUR times ;; (defun encode-string-for-title (string) (declare (simple-string string)) (if (find #\' string :test #'char=) (let ((output (make-array (truncate (length string) 2/3) :element-type 'character :adjustable t :fill-pointer 0))) (with-output-to-string (out output) (loop for char across string do (case char ;; (code-char 39) ==> #\' ((#\') (write-string "'" out)) (t (write-char char out))))) (coerce output 'simple-string)) string)) (defun find-include-file (pathname) (bind ((pathname (ensure-string pathname)) (search-locations (ensure-list (document-property :search-locations))) (result (or (probe-file (merge-pathnames pathname)) ;; look in search-locations (some (lambda (location) (probe-file (merge-pathnames pathname location))) search-locations)))) (unless result (markdown-warning "Unable to find ~a in any of the search-locations ~{~a~^, ~}" pathname search-locations)) result)) (defun process-child-markdown (text phase &key (transfer-data nil)) (bind (((:values child output) (markdown text :parent *current-document* :format (if (eq phase :parse) :none *current-format*) :properties '((:omit-initial-paragraph t) (:omit-final-paragraph t)) :stream nil :document-class 'included-document))) (push child (children *current-document*)) (when transfer-data (transfer-link-info *current-document* child "") (transfer-document-metadata *current-document* child) (transfer-selected-properties *current-document* child (set-difference (collect-keys (properties child)) (list :omit-initial-paragraph :omit-final-paragraph)))) (ecase phase (:parse child) (:render (strip-whitespace output))))) (defun asdf-system-source-file (system-name) (let ((system (asdf:find-system system-name))) (make-pathname :type "asd" :name (asdf:component-name system) :defaults (asdf:component-relative-pathname system)))) (defun asdf-system-source-directory (system-name) (make-pathname :name nil :type nil :defaults (asdf-system-source-file system-name))) (defun system-relative-pathname (system pathname &key name type) (relative-pathname (asdf-system-source-directory system) pathname :name name :type type)) cl-markdown-20101006-darcs/dev/dead-code/0000755000175000017500000000000011453110206016261 5ustar xachxachcl-markdown-20101006-darcs/dev/dead-code/lml2.lisp0000644000175000017500000001545511453110206020032 0ustar xachxach(in-package #:cl-markdown) (defparameter *markup->lml2* (make-container 'simple-associative-container :test #'equal :initial-contents '((header1) (nil :h1) (header2) (nil :h2) (header3) (nil :h3) (header4) (nil :h4) (header5) (nil :h5) (header6) (nil :h6) (bullet) ((:ul) :li) (code) ((:pre :code) nil) (number) ((:ol) :li) (quote) ((:blockquote) nil) (horizontal-rule) (nil :hr)))) ;;; --------------------------------------------------------------------------- (defmethod render ((document document) (style (eql :lml2)) stream) (let ((*current-document* document)) (setf (level document) 0 (markup document) nil) (let* ((chunks (collect-elements (chunks document))) (result (lml2-list->tree chunks))) (if stream (format stream "~S" result) result)))) ;;; --------------------------------------------------------------------------- (defun lml2-marker (chunk) (bind ((markup (markup-class-for-lml2 chunk))) (first markup))) ;;; --------------------------------------------------------------------------- (defmethod render-to-lml2 ((chunk chunk)) (bind ((block (collect-elements (lines chunk) :transform (lambda (line) (render-to-lml2 line)))) (markup (second (markup-class-for-lml2 chunk))) (paragraph? (paragraph? chunk))) (cond ((and paragraph? markup) (values `(,markup (:P ,@block)) t)) (paragraph? (values `(:P ,@block) t)) (markup (values `(,markup ,@block) t)) (t (values block nil))))) ;;; --------------------------------------------------------------------------- (defmethod markup-class-for-lml2 ((chunk chunk)) (when (markup-class chunk) (let ((translation (item-at-1 *markup->lml2* (markup-class chunk)))) (unless translation (warn "No translation for '~A'" (markup-class chunk))) translation))) ;;; --------------------------------------------------------------------------- (defmethod render-to-lml2 ((chunk list)) (render-span-to-lml2 (first chunk) (rest chunk))) ;;; --------------------------------------------------------------------------- (defmethod render-to-lml2 ((chunk string)) ;;?? unlovely (format nil "~A" chunk)) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'strong)) body) `(:strong ,@body)) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'mail)) body) (let ((address (first body))) `((:a :href ,(format nil "mailto:~A" address)) ,address))) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'emphasis)) body) `(:em ,@body)) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'strong-em)) body) `(:strong (:em ,@body))) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'code)) body) `(:code ,(render-to-lml2 (first body)))) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'entity)) body) (first body)) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'reference-link)) body) (bind (((text &optional (id text)) body) (link-info (item-at-1 (link-info *current-document*) id))) (if link-info `((:a :href ,(url link-info) ,@(awhen (title link-info) `(:title ,it))) ,text) `,text))) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'inline-link)) body) (bind (((text &optional (url "") title) body)) `((:a :href ,url ,@(awhen title `(:title ,it))) ,text))) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'link)) body) (bind ((url body)) `((:a :href ,@url) ,@url))) ;;; --------------------------------------------------------------------------- (defmethod render-span-to-lml2 ((code (eql 'html)) body) (html-encode:encode-for-pre (first body))) (defun lml2-list->tree (chunks &key (level nil)) (unless level (setf level (or (and (first chunks) (level (first chunks))) 0))) (labels ((do-it (chunks level) ;;?? rather inpenetrable... don't understand at the level I should... (apply-mark (lml2-marker (first chunks)) (let (output append? result) (loop for rest = chunks then (rest rest) for chunk = (first rest) then (first rest) while chunk for new-level = (level chunk) do (setf (values output append?) (render-to-lml2 chunk)) do (format t "~%C(~D/~D): ~A, ~A" level new-level append? chunk) when (and (= level new-level) append?) do (setf result `(,output ,@result)) when (and (= level new-level) (not append?)) do (setf result `(,@output ,@result)) when (< level new-level) do (multiple-value-bind (block remaining method) (next-block rest new-level) (let ((inner (do-it block (1+ level)))) ; (format t "~%--- ~A" method) (setf rest remaining) (ecase method (:level (if (listp (first result)) (push-end inner (first result)) (push inner result))) (:markup (push inner result)) (:none (setf result `(,inner ,@result)))))) when (> level new-level) do (warn "unexpected chunk level")) (reverse result))))) (apply #'do-it chunks level))) ;;; --------------------------------------------------------------------------- (defun apply-mark (mark rest) (cond ((null mark) rest) ((consp mark) (if (length-1-list-p mark) `(,(first mark) ,@(apply-mark (rest mark) rest)) `(,(first mark) (,@(apply-mark (rest mark) rest))))) (t (error "unhandled case")))) cl-markdown-20101006-darcs/dev/macros.lisp0000644000175000017500000000654011453110206016636 0ustar xachxach(in-package #:cl-markdown) (defmacro defsimple-extension (name &body body) "Create an extension (a function named `name`) with no arguments that does not depend on the markdown phase and which does not use the result. These are handy for simple text substitutions." (with-gensyms (phase arguments result) `(progn (pushnew (list ',name t) *extensions* :key #'car) (defun ,name (,phase ,arguments ,result) (declare (ignore ,phase ,arguments ,result)) ,@body) ,@(%import/export-symbol name)))) (defun %validate-defextension-arguments (arguments) (loop for argument in (ensure-list arguments) do (cond ((atom argument) (when (eq (symbol-package argument) #.(find-package :keyword)) (error "Argument names may not be keywords and ~s is not" argument))) (t (unless (every (lambda (facet) (member facet '(:required :keyword :whole))) (rest argument)) (error "Invalid argument facets in ~s" (rest argument))))))) (defun %collect-arguments (arguments kind) (loop for argument in (ensure-list arguments) when (and (consp argument) (member kind (rest argument))) collect (first argument))) (defun %collect-positionals (arguments) (loop for argument in (ensure-list arguments) when (or (atom argument) (and (consp argument) (not (member :keyword (rest argument))))) collect (first (ensure-list argument)))) (defparameter *extensions* nil) (defmacro defextension ((name &key arguments (insertp nil) (exportp t)) &body body) (%validate-defextension-arguments arguments) (bind ((keywords (%collect-arguments arguments :keyword)) (requires (%collect-arguments arguments :required)) (whole (%collect-arguments arguments :whole)) (positionals (%collect-positionals arguments))) (assert (<= (length whole) 1) nil "At most one :whole argument is allowed.") (assert (null (intersection whole keywords)) nil "Keyword arguments cannot be wholes") `(progn (setf *extensions* (remove ',name *extensions* :key #'first)) (push (list ',name ,insertp) *extensions*) (defun ,name (phase args result) (declare (ignorable phase args result)) (bind (,@(loop for positional in positionals unless (member positional whole) collect `(,positional (pop args))) ,@(loop for keyword in keywords collect `(,keyword (getf args ,(intern (symbol-name keyword) :keyword) nil))) ,@(when whole `((,(first whole) ;; remove keywords from args (progn ,@(loop for keyword in keywords collect `(,keyword (remf args ,(intern (symbol-name keyword) :keyword)))) (if (length-1-list-p args) (first args) args)))))) ,@(loop for require in requires collect `(assert ,require nil ,(format nil "~s is required" require))) ,@body ,@(unless insertp nil))) ,@(when exportp (%import/export-symbol name))))) (defun %import/export-symbol (name) `((eval-when (:compile-toplevel :load-toplevel :execute) (import ',name ,(find-package :cl-markdown-user)) (export ',name ,(find-package :cl-markdown-user))))) (defmacro aand+ (&rest args) "Anaphoric nested AND. Binds the symbol `it' to the value of the preceding `arg.'" (cond ((null args) t) ((null (cdr args)) (car args)) (t `(aif ,(car args) (aand ,@(cdr args)))))) cl-markdown-20101006-darcs/dev/definitions.lisp0000644000175000017500000000200011453110206017650 0ustar xachxach(in-package #:cl-markdown) (defparameter *spaces-per-tab* 4) (defparameter *parsing-environment* nil) (defparameter *chunk-parsing-environments* (make-container 'simple-associative-container)) (defparameter *spanner-parsing-environments* (make-container 'simple-associative-container :test #'equal)) (defparameter *horizontal-rule-count-threshold* 3) (defparameter *default-stream* *standard-output*) (defparameter *default-format* :html) (defvar *output-stream* nil) (defvar *current-indentation-level* 0) (defparameter *current-document* nil) (defparameter *current-format* nil) (defparameter *render-active-functions* '(table-of-contents property set-property anchor footnote footnotes today now include include-if comment)) (defparameter *parse-active-functions* '(table-of-contents property set-property anchor footnote footnotes include include-if comment)) (defparameter *block-level-html-tags* '(address blockquote div fieldset h1 h2 h3 h4 h5 h6 hr legend p pre ul ol li dl dd)) cl-markdown-20101006-darcs/dev/html.lisp0000644000175000017500000004131211453110206016312 0ustar xachxach(in-package #:cl-markdown) (defstruct (html-markup (:conc-name markup-) (:print-object print-html-markup)) name outer inner tag encoding-method contentlessp (nestsp t)) (defmethod print-html-markup (markup stream) (print-unreadable-object (markup stream :type nil :identity t) (bind (((:struct markup- name inner outer encoding-method) markup)) (format stream "~a : ~a ~a ~a" name outer inner encoding-method)))) (defparameter *markup->html* (make-container 'simple-associative-container :test #'equal :initial-contents (loop for datum in '((header1 nil nil "h1") (header2 nil nil "h2") (header3 nil nil "h3") (header4 nil nil "h4") (header5 nil nil "h5") (header6 nil nil "h6") (bullet ("ul") ("li") nil :nestsp t) (code ("pre" "code") nil nil :new-method encode-pre) (number ("ol") ("li") nil :nestsp nil) (quote ("blockquote") nil nil) (horizontal-rule nil nil "hr" :contentlessp t)) nconc (bind (((tag outer inner markup &key new-method contentlessp (nestsp t)) datum)) (list (list tag) (make-html-markup :name tag :outer outer :inner inner :tag markup :encoding-method new-method :contentlessp contentlessp :nestsp nestsp)))))) (defvar *magic-space-p* nil) (defvar *magic-line-p* 0) (defparameter *magic-space* #\Space) (defparameter *magic-line* nil) #+(or) (setf *magic-space* #\| *magic-line* #\~) (defgeneric render-to-html (stuff encoding-method) (:documentation "")) (defmethod render ((document abstract-document) (style (eql :html)) stream) (declare (ignore stream)) (setf *magic-space-p* nil *magic-line-p* 0) (render-to-html document nil)) (defun html-block-markup (chunk) (aand+ (markup-class-for-html chunk) (markup-outer it))) (defun html-inner-block-markup (chunk) (aand (markup-class-for-html chunk) (markup-inner it))) (defmethod render-to-html ((chunk chunk) encoding-method) (bind (((:struct markup- (markup tag) (new-method encoding-method) contentlessp) (markup-class-for-html chunk)) (paragraph? (paragraph? chunk))) (cond (contentlessp (format *output-stream* "<~a/>" markup)) (t (encode-html chunk (or new-method encoding-method) markup (when paragraph? "p")))))) ;;?? same code as below (defmethod encode-html ((stuff chunk) encoding-method &rest codes) (declare (dynamic-extent codes)) (setf *magic-line-p* 0) (cond ((null codes) (let ((code-p (member 'code (markup-class stuff)))) (iterate-elements (lines stuff) (lambda (line) (render-to-html line encoding-method) (when code-p (incf *magic-line-p*)))) ;(fresh-line *output-stream*) )) ((null (first codes)) (apply #'encode-html stuff encoding-method (rest codes))) (t (format *output-stream* "<~a>" (first codes)) (apply #'encode-html stuff encoding-method (rest codes)) (format *output-stream* "" (first codes))))) ;;?? same code as above (defmethod encode-html ((stuff list) encoding-method &rest codes) (declare (dynamic-extent codes)) (cond ((null codes) (iterate-elements stuff (lambda (line) (render-to-html line encoding-method)))) ((null (first codes)) (apply #'encode-html stuff encoding-method (rest codes))) (t (format *output-stream* "<~A>" (first codes)) (apply #'encode-html stuff encoding-method (rest codes)) (format *output-stream* "" (first codes))))) (defmethod markup-class-for-html ((chunk chunk)) (if (markup-class chunk) (let ((translation (item-at-1 *markup->html* (markup-class chunk)))) (unless translation (markdown-warning "No translation for markup class '~A'" (markup-class chunk))) translation) (load-time-value (make-html-markup)))) (defmethod render-to-html ((chunk list) encoding-method) (render-span-to-html (first chunk) (rest chunk) encoding-method)) (defmethod render-to-html ((line string) encoding-method) (when *magic-space-p* (setf *magic-space-p* nil) (princ *magic-space* *output-stream*)) (when (> *magic-line-p* 0) (when *magic-line* (princ *magic-line* *output-stream*)) (terpri *output-stream*)) (format *output-stream* "~a" (funcall (or encoding-method 'encode-string-for-html) line)) (setf *magic-space-p* t)) (defun output-html (string &rest codes) (declare (dynamic-extent codes)) (cond ((null codes) (princ (first string) *output-stream*)) (t (format *output-stream* "<~(~a~)>" (first codes)) (apply #'output-html string (rest codes)) (format *output-stream* "" (first codes))))) (defmethod render-span-to-html ((code (eql 'strong)) body encoding-method) (declare (ignore encoding-method)) (output-html body 'strong) (setf *magic-space-p* nil)) (defmethod render-span-to-html ((code (eql 'mail)) body encoding-method) (declare (ignore encoding-method)) (let ((address (first body))) (output-link (format nil "mailto:~A" address) nil address))) (defmethod render-span-to-html ((code (eql 'emphasis)) body encoding-method) (declare (ignore encoding-method)) (output-html body 'em) (setf *magic-space-p* nil)) (defmethod render-span-to-html ((code (eql 'strong-em)) body encoding-method) (declare (ignore encoding-method)) (output-html body 'strong 'em) (setf *magic-space-p* nil)) (defmethod render-span-to-html ((code (eql 'escaped-character)) body encoding-method) (declare (ignore encoding-method)) (let ((char (aref (first body) 0))) (cond ((char= #\< char) (princ "<" *output-stream*)) ((char= #\> char) (princ ">" *output-stream*)) (t (output-html body)))) (setf *magic-space-p* nil)) (defmethod render-span-to-html ((code (eql 'code)) body encoding-method) (declare (ignore encoding-method)) (format *output-stream* "") (setf *magic-space-p* nil) (dolist (bit body) (render-to-html bit 'encode-pre)) (format *output-stream* "") (setf *magic-space-p* nil)) (defmethod render-span-to-html ((code (eql 'entity)) body encoding-method) (declare (ignore encoding-method)) (setf *magic-space-p* nil *magic-line-p* -1) (output-html body) (setf *magic-space-p* nil *magic-line-p* -1)) (defmethod render-span-to-html ((code (eql 'reference-link)) body encoding-method) (declare (ignore encoding-method)) (bind (((:values text id nil) (if (length-1-list-p body) (values (first body) (first body) nil) (values (butlast body 1) (first (last body)) t))) (link-info (find-link id))) (cond ((not (null link-info)) ;; it _was_ a valid ID (generate-link-output link-info text)) (t (markdown-warning "No reference found for link ~s" id) (format *output-stream* "~a" (if (consp text) (first text) text)) (setf *magic-space-p* nil))))) (defmethod generate-link-output ((link-info link-info) text) (output-link (url link-info) (title link-info) text (properties link-info))) (defmethod generate-link-output ((link-info extended-link-info) text) ;; you didn't really want it to be fast did you...? (generate-link-output-for-kind (kind link-info) link-info text)) (defmethod render-span-to-html ((code (eql 'inline-link)) body encoding-method) (declare (ignore encoding-method)) (bind (((text &optional (url "") title) body)) (output-link url title (list text)))) (defmethod render-span-to-html ((code (eql 'link)) body encoding-method) (declare (ignore encoding-method)) (let ((link (first body))) (output-link link nil link))) (defmethod render-span-to-html ((code (eql 'reference-image)) body encoding-method) (declare (ignore encoding-method)) (bind (((:values text id nil) (if (length-1-list-p body) (values (first body) (first body) nil) (values (butlast body 1) (first (last body)) t))) (link-info (find-link id))) (cond ((not (null link-info)) ;; it _was_ a valid ID (output-image (url link-info) (title link-info) text (properties link-info))) (t ;;?? hackish (markdown-warning "No reference found for image link ~s" id) (format *output-stream* "~a (image)" (if (consp text) (first text) text)) (setf *magic-space-p* nil))))) (defmethod render-span-to-html ((code (eql 'inline-image)) body encoding-method) (declare (ignore encoding-method)) (bind (((text &optional (url "") title) body)) (output-image url title text))) (defun output-link (url title text &optional properties) (cond ((not (null url)) (format *output-stream* "" *output-stream*) (setf *magic-space-p* nil) (encode-html (ensure-list text) nil) (format *output-stream* "") (setf *magic-space-p* nil)) (t ))) (defun output-image (url title text &optional properties) (cond ((not (null url)) (format *output-stream* "\"~A\"~]"" *output-stream*) (setf *magic-space-p* nil)) (t ))) (defmethod render-span-to-html ((code (eql 'html)) body encoding-method) ;; hack! (let ((output (first body))) (etypecase output (string (output-html (list output #+(or) (encode-pre output)))) (list (render-span-to-html (first output) (rest output) encoding-method))))) ;; Special cases R us. (defmethod render-span-to-html ((code (eql 'break)) body encoding-method) (encode-html body encoding-method) (format *output-stream* "
    ~%")) (defun stream-out-markup (markup reverse) (dolist (marker (if reverse (reverse markup) markup)) (let ((cr? (member marker *block-level-html-tags* :test 'string=))) (when (and (not reverse) cr?) (terpri *output-stream*)) (format *output-stream* "<~a~a>" (if reverse "/" "") marker)))) ;; FIXME - in case you're wondering, this is an ugly bit of code (defmethod render-to-html ((document abstract-document) encoding-method) (bind ((current-chunk nil) (wrap-in-html (add-html-header-p document))) (labels ((render-block (block level markup inner?) ; (print (list :rb level inner? (first block))) (setf *magic-space-p* nil) (let ((add-markup? (not (eq (first block) current-chunk))) (real-markup (if (and (not inner?) (length-1-list-p block)) (append markup (html-inner-block-markup (first block))) markup))) (when add-markup? (stream-out-markup real-markup nil)) (cond ((or (length-1-list-p block) ) (render-to-html (first block) encoding-method)) ((not add-markup?) (render-to-html (first block) encoding-method) (do-it (rest block) level)) (t (setf current-chunk (and inner? (first block))) (do-it block level))) (when add-markup? (stream-out-markup real-markup t)))) (do-it (chunks level) ; (print (list :di level (first chunks))) (loop for rest = chunks then (rest rest) for chunk = (first rest) then (first rest) while chunk for new-level = (level chunk) when (= level new-level) do (let ((index (inner-block rest)) (inner-markup (html-inner-block-markup chunk))) (render-block (subseq rest 0 index) level inner-markup t) (setf rest (nthcdr (1- index) rest))) when (< level new-level) do (multiple-value-bind (block remaining method) (next-block rest new-level) (declare (ignore method)) (render-block block new-level (html-block-markup chunk) nil) (setf rest remaining))))) (when wrap-in-html (generate-html-header)) (do-it (collect-elements (chunks document)) (level document)) (when wrap-in-html (format *output-stream* "~&~&~%"))))) (defmethod add-html-header-p ((document abstract-document)) (values nil)) (defmethod add-html-header-p ((document document)) (document-property :html)) (defun inner-block (chunks) (bind ((level (level (first chunks))) (markup-class (markup-class (first chunks))) (nestsp (aand+ (markup-class-for-html (first chunks)) (markup-nestsp it)))) (or ;; if we go down a level immediately after, take whereever we go back up ;; or the end of the document. ;; FIXME - I think we're trying to find the _end_ of the _block_ ;; and this would mean keeping track of nesting until we actually ;; come all the way "up" and out. Need a test case for this (aand+ nestsp (let ((next-chunk (first (rest chunks)))) (and next-chunk (or (> (level next-chunk) level) (and (= (level next-chunk) level) (not (equal (markup-class next-chunk) markup-class)))))) #+(or) (position-if (lambda (chunk) (or (> (level chunk) level) (and (= (level chunk) level) (not (equal (markup-class chunk) markup-class))))) (rest chunks)) (aif (position-if (lambda (chunk) (<= (level chunk) level)) (rest chunks) :start 1 #+(or) (1+ it)) (1+ it) (length chunks))) ;; do we go up a level or change markup classes at the same level (aand+ (position-if (lambda (chunk) (or (< (level chunk) level) (and (= (level chunk) level) (not (equal (markup-class chunk) markup-class))))) (rest chunks)) (1+ it)) 1))) (defvar *html-meta* '((name (author description copyright keywords date)) (http-equiv (refresh expires |Content-Type|)))) ;; (defun generate-html-header () (generate-doctype) (format *output-stream* "~&" (document-property :xmlns "http://www.w3.org/1999/xhtml") (document-property :xmllang "en") (document-property :lang "en")) (format *output-stream* "~&") (when (document-property :header-comment) (format *output-stream* "~&~%" (nth-value 1 (markdown (document-property :header-comment) :properties '((:omit-initial-paragraph t) (:omit-final-paragraph t) (:html . nil)) :format :plain :stream nil)))) (awhen (document-property "title") (format *output-stream* "~&~a" (process-child-markdown it :render :transfer-data nil))) (let ((styles nil)) (flet ((output-style (it) (bind (((name &optional media) (ensure-list it))) (setf name (ensure-string name)) (unless (search ".css" name) (setf name (concatenate 'string name ".css"))) (unless (member name styles :test 'string-equal) (push name styles) (if (document-property :make-style-sheet-inline) (insert-style-sheet name media) (format *output-stream* "~&" name media)))))) (awhen (document-property "style-sheet") (output-style it)) (loop for style in (document-property "style-sheets") do (output-style style)) #+never ; This original loop form contained a free reference to `it'. (loop for (kind properties) in *html-meta* do (loop for property in properties do (when (document-property (symbol-name property)) (format *output-stream* "~&" kind property it)))) ; Rewritten smh 2008-05-`3 (loop for (kind properties) in *html-meta* do (loop for property in properties as val = (document-property (symbol-name property)) when val do (format *output-stream* "~&" kind property val))) (format *output-stream* "~&~&" (document-property :markdown-body-id))))) (defun generate-doctype () (format *output-stream* "~&" "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd")) #| "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |# (defun insert-style-sheet (name media) (let ((pathname (find-include-file name))) (when pathname (format *output-stream* "~&~%")))) (defun output-anchor (name &optional (stream *output-stream*)) (let ((name (html-safe-name (ensure-string name)))) (format stream "~&~%" name name))) cl-markdown-20101006-darcs/dev/notes.text0000644000175000017500000000170111453110206016511 0ustar xachxach### Notes - We go from md to document to html We can't really :check (:compare) or :remove unless we have a way to go from html to document or html to md. - How to handle multi-line responses. from doctest (documentation)[http://docs.python.org/lib/doctest-finding-examples.html] > Any expected output must immediately follow the final '>>> ' or '... ' line containing the code, and the expected output (if any) extends to the next '>>> ' or all-whitespace line. (there are lots of other good ideas in this document). Well known properties :search-locations :title :style-sheet :docs-package html style-sheets author docs-space-entity (document-property :xmlns "http://www.w3.org/1999/xhtml") (document-property :xmllang "en") (document-property :lang "en")) (document-property :header-comment) ### Problems This causes an error {today} causing an error in footnote cl-markdown-20101006-darcs/dev/extension-mechanisms.lisp0000644000175000017500000001450311453110206021511 0ustar xachxach(in-package #:cl-markdown) #| extensions should have a unique name and a priority (as should the built-ins) |# ;;?? only add once (defun add-extension (extension &key (filter (constantly t))) (iterate-key-value *spanner-parsing-environments* (lambda (key value) (when (funcall filter key) (insert-new-item value extension))))) #| (markdown "Hello {user-name :format :long}, how are you. Go {{here}}." :format :none) ==> '("Hello " (EVAL "user-name :format :long") ", how are you. Go " (MARKDOWN::WIKI-LINK "here") ". ") (let ((*render-active-functions* (append '(today now) *render-active-functions*))) (markdown "Today is {today}. It is {now}." :format :html :stream t)) |# (define-parse-tree-synonym wiki-link (:sequence #\{ #\{ (:register (:greedy-repetition 0 nil (:inverted-char-class #\}))) #\} #\})) (define-parse-tree-synonym eval (:sequence #\{ (:register (:greedy-repetition 0 nil (:inverted-char-class #\}))) #\})) (define-parse-tree-synonym eval-in-code (:sequence #\{ #\{ (:register (:greedy-repetition 0 nil (:inverted-char-class #\}))) #\} #\})) ;; should only happen once! (but need names to do this correctly) (eval-when (:load-toplevel :execute) #+(or) (add-extension (list (create-scanner '(:sequence wiki-link)) 'wiki-link) :filter (lambda (key) (not (equal key '(code))))) (add-extension (make-markdown-scanner :regex (create-scanner '(:sequence eval)) :name 'eval :priority 1.5) :filter (lambda (key) (not (equal key '(code))))) (add-extension (make-markdown-scanner :regex (create-scanner '(:sequence eval-in-code)) :name 'code-eval :priority 1.5) :filter (lambda (key) (equal key '(code))))) (defmethod render-span-to-html ((code (eql 'eval)) body encoding-method) (declare (ignore encoding-method)) (render-handle-eval body)) (defmethod render-span-to-html ((code (eql 'code-eval)) body encoding-method) (declare (ignore encoding-method)) (render-handle-eval body)) (defun render-handle-eval (body) ;;?? parse out commands and arguments (deal with quoting, etc) (bind (((command arguments result nil #+(or) processed?) body) (result (cond ((and (member command *render-active-functions*) (fboundp command)) (funcall command :render arguments (ensure-list result))) ((and (member command *render-active-functions*) (not (fboundp command))) (warn "Undefined CL-Markdown function ~s" command)) (t nil)))) (when result (output-html (list result)) (setf *magic-space-p* nil) (setf *magic-line-p* -1)))) (defun canonize-command (command) (intern (symbol-name command) (load-time-value (find-package :cl-markdown-user)))) (defmethod process-span ((name (eql 'eval)) registers) ;; the one register contains the command and the buffer index. (bind (((command &rest args) (%pull-arguments-from-string (first registers))) (buffer-index (and args (fixnump (first args)) (first args)))) (process-handle-eval command (or (and buffer-index (%pull-arguments-from-string (item-at (bracket-references *current-document*) buffer-index))) args)))) (defmethod process-span ((name (eql 'code-eval)) registers) ;;; the one register contains the command and all its arguments as one ;; big string we tokenize it and make sure the command exists and, if ;; it is 'active' during parsing, we call it for effect. (bind (((command &rest arguments) (%pull-arguments-from-string (first registers)))) (process-handle-eval command arguments))) (defun process-handle-eval (command arguments) (bind ((command (canonize-command command)) ((:values result processed?) (when (member command *parse-active-functions*) (if (fboundp command) (values (funcall command :parse arguments nil) t) (warn "Undefined CL-Markdown parse active function ~s" command))))) #+(or) (format t "~&~s: ~s ~a ~a" command (symbol-package command) (fboundp command) (member command *parse-active-functions*) result) `(,command ,arguments ,result ,processed?))) (defmethod process-span-in-span-p ((span-1 t) (span-2 (eql 'eval))) (values nil)) (defmethod process-span-in-span-p ((span-1 t) (span-2 (eql 'code-eval))) (values nil)) (defun %pull-arguments-from-string (string) (let ((start 0) (done (load-time-value (list :eof))) (result nil)) (loop collect (multiple-value-bind (value new-start) (ignore-errors (read-from-string string nil done :start start)) (when (eq value done) (return)) (cond ((and new-start (numberp new-start)) (setf start new-start) (push value result)) (t (incf start))))) (nreverse result))) ;;;;; #| Another extension mechanism |# (defmethod generate-link-output-for-kind ((kind (eql :glossary)) (link-info extended-link-info) text) (let ((text (if (consp text) (first text) text))) (format *output-stream* "~a" (id link-info) text text))) (defextension (glossary) (when (eq phase :render) (format *output-stream* "~&
    ") (format *output-stream* "~&
    ") (iterate-key-value (link-info *current-document*) (lambda (key link) (when (and (typep link 'extended-link-info) (eq (kind link) :glossary)) (format *output-stream* "~&
    ~a
    " key (id link)) (markdown (contents link) :stream *output-stream* :format *current-format* :properties '((:html . nil) (:omit-final-paragraph . t) (:omit-initial-paragraph . t)) :document-class 'included-document) (format *output-stream* "
    ")))) (format *output-stream* "
    ~%
    ~%"))) ;;; sort of works ;; can't use html in title (defmethod generate-link-output-for-kind ((kind (eql :abbreviation)) (link-info extended-link-info) text) (let ((output (nth-value 1 (markdown (contents link-info) :stream nil :document-class 'included-document)))) (format *output-stream* "~a" output text))) cl-markdown-20101006-darcs/dev/package.lisp0000644000175000017500000000243711453110206016746 0ustar xachxach(in-package #:common-lisp-user) (defpackage #:cl-markdown (:use #:common-lisp #:metatilities #:cl-containers #:cl-ppcre #:metabang-bind #:anaphora) (:export #:handle-spans #:markdown #:markdown-many #:render-to-stream #:*current-document* #:*output-stream* #:document-property) (:nicknames #:markdown) (:export #:*render-active-functions* #:*parse-active-functions* #:anchor #:table-of-contents #:property #:set-property #:render #:render-documentation) ;; handy (?) regular expressions (:export #:emphasis-1 #:emphasis-2 #:strong-1 #:strong-2 #:backtick #:auto-link #:auto-mail #:html #:entity #:hostname-char #:hostname #:pathname-char #:url-pathname #:url #:url-no-registers #:bracketed #:link+title #:reference-link #:inline-link #:link-label) (:export #:footnote #:footnotes #:find-documentation #:add-documentation-strategy #:defextension #:defsimple-extension)) (defpackage #:cl-markdown-user (:use #:common-lisp #:metatilities #:cl-markdown) (:import-from #:cl-markdown #:footnote #:footnotes #:defextension #:defsimple-extension ) (:export #:footnote #:footnotes #:find-documentation #:add-documentation-strategy #:defextension #:defsimple-extension )) cl-markdown-20101006-darcs/dev/extensions.lisp0000644000175000017500000002006211453110206017544 0ustar xachxach(in-package #:cl-markdown) ;; {f a0 .. an} ;; -> eval f a0 .. an -- where ai are strings ;; -> returns string that is inserted into document ;; -> or nil (cound do insertions itself) ;; no recursive function embeddings {date-stamp {today}} ;; keywords handled separately? ;; could use a macro ;; ;; to specify a name, arguments, etc and use that to parse. and export (defsimple-extension today (let ((format (document-property :date-format "%e %B %Y"))) (format-date format (get-universal-time)))) (defsimple-extension now (let ((format (document-property :time-format "%H:%M"))) (format-date format (get-universal-time)))) (defextension (comment :arguments ((text :required)) :insertp t) (ecase phase (:parse ;; no worries ) (:render (format nil "" text)))) (defextension (remark :arguments ((text :required)) :insertp t) (ecase phase (:parse ;; no worries ) (:render ;; stil no worries ))) (defextension (anchor :arguments ((name :required) title) :insertp t) (setf name (ensure-string name)) (let ((safe-name (html-safe-name name))) (ecase phase (:parse (setf (item-at (link-info *current-document*) name) (make-instance 'link-info :id name :url (format nil "#~a" safe-name) :title (or title "")))) (:render (format nil "" safe-name safe-name))))) (defextension (property :arguments ((name :required)) :insertp t) (ecase phase (:parse) (:render (process-child-markdown (document-property name) phase)))) (defextension (ifdef :arguments ((keys :required) (text :required :whole)) :insertp t) (ecase phase (:parse) (:render (prog1 (if (or (and (atom keys) (document-property keys)) ) (process-child-markdown (format nil "~{~a~^ ~}" (ensure-list text)) phase) ""))))) #| (defvar *x*) (defextension (property :arguments ((name :required))) (ecase phase (:parse) (:render (bind (((:values d s) (markdown (document-property name) :parent *current-document* :format *current-format* :properties '((:omit-initial-paragraph t) (:omit-final-paragraph t) (:html . nil)) :stream nil))) (setf *x* d) (prog1 (strip-whitespace s)))))) (let ((*current-document* *x*)) (document-property "html")) (form-property-name "html") (trace item-at-1) |# (defextension (set-property :arguments ((name :required) (value :whole)) :insertp t) (when (eq phase :parse) (setf (document-property name) value)) nil) #+(or) ;;?? (defun set-property (phase args result) (declare (ignorable phase args result)) (bind ((name (pop args)) (value (progn (if (length-1-list-p args) (first args) args)))) (assert name nil "name is required") (when (eq phase :parse) (setf (document-property name) value)) nil)) (defextension (table-of-contents :arguments ((depth :required :keyword) (start :required :keyword) (label :keyword)) :insertp t) (ecase phase (:parse (push (lambda (document) (add-toc-anchors document :depth depth :start start)) (item-at-1 (properties *current-document*) :cleanup-functions)) nil) (:render (bind ((headers (collect-toc-headings depth start))) (when headers (format *output-stream* "~&") (format *output-stream* "~&
    ~%") (when label (format *output-stream* "

    ~a

    " label)) (iterate-elements headers (lambda (header) (bind (((nil anchor text) (item-at-1 (properties header) :anchor)) (save-header-lines (copy-list (lines header)))) (setf (slot-value header 'lines) `(,(format nil "~&" (if (char= (aref anchor 0) #\#) "" "#") anchor (encode-string-for-title text)) ,@(lines header) ,(format nil ""))) (render-to-html header nil) (setf (slot-value header 'lines) save-header-lines)))) (format *output-stream* "~&
    ~%")))))) (defun collect-toc-headings (depth start) (collect-elements (chunks *current-document*) :filter (lambda (x) (header-p x :depth depth :start start)))) (defsimple-extension toc-link (format nil "~&Top")) (defun make-ref (index level) (format nil "~(~a-~a~)" level index)) (defun add-toc-anchors (document &key depth start) (let* ((index -1) (header-level nil) (last-anchor nil) (header-indexes (nreverse (collect-elements (chunks document) :transform (lambda (chunk) (item-at-1 (properties chunk) :anchor)) :filter (lambda (chunk) (incf index) (let ((it nil)) (cond ((setf it (header-p chunk :depth depth :start start)) (setf header-level it) (setf (item-at-1 (properties chunk) :anchor) (list index (or (and last-anchor (url last-anchor)) (make-ref index header-level)) (with-output (*output-stream* nil) (render-plain chunk)))) (null last-anchor)) ((setf it (simple-anchor-p chunk)) (setf last-anchor it) nil) (t (setf last-anchor nil))))))))) (iterate-elements header-indexes (lambda (datum) ; (print datum) (bind (((index ref text) datum)) (anchor :parse `(,ref ,text) nil) (insert-item-at (chunks document) (make-instance 'chunk :lines `((eval anchor (,ref nil) nil t))) index)))))) (defun simple-anchor-p (chunk) (and (< 0 (size (lines chunk)) 3) (length-at-least-p (first-element (lines chunk)) 2) (equal (subseq (first-element (lines chunk)) 0 2) '(eval anchor)) (fourth (first-element (lines chunk))))) (defun header-p (chunk &key depth start) (let* ((header-elements '(header1 header2 header3 header4 header5 header6)) (header-elements (subseq header-elements (1- (or start 1)) (min (or depth (length header-elements)) (length header-elements))))) (some-element-p (markup-class chunk) (lambda (class) (member class header-elements))))) #+(or) (markdown "{set-property html t} html = {property html} I like {docs markdown function}, don't you." :additional-extensions '(docs)) #+(or) (markdown "{set-property docs-package asdf-install} {set-property {docs install function} {docs asdf-install:*gnu-tar-program* variable} " :additional-extensions '(docs)) #| {set-property docs-package asdf-install} {set-property docs-heading-level 4} {set-property docs-heading-format "%type %name:"} {docs *gnu-tar-program* variable} |# (defextension (abbrev :arguments ((abbreviation :required) (text :required :whole))) (ecase phase (:parse ;; no worries ) (:render (format nil "~a" abbreviation)))) (defextension (include :arguments ((pathname :required)) :insertp t) (ecase phase (:parse ;; no worries (let ((pathname (find-include-file pathname))) ;; FIXME - if I use a list, someone in markdown calls chunks on it. (make-array 1 :initial-contents (list (process-child-markdown pathname phase :transfer-data t))))) (:render (when result (render-to-stream (aref (first result) 0) *current-format* nil))))) (defextension (include-if :arguments ((test :required) (pathname :required)) :insertp t) (ecase phase (:parse (when (document-property test) (let ((pathname (find-include-file pathname))) ;; FIXME - if I use a list, someone in markdown calls chunks on it. (make-array 1 :initial-contents (list (process-child-markdown pathname phase :transfer-data t)))))) (:render (when result (render-to-stream (aref (first result) 0) *current-format* nil))))) cl-markdown-20101006-darcs/dev/regexes.lisp0000644000175000017500000001561411453110206017016 0ustar xachxach(in-package #:markdown) (define-parse-tree-synonym line-ends-with-two-spaces (:sequence (:register (:sequence (:greedy-repetition 0 nil :everything))) #\Space #\Space :end-anchor)) (define-parse-tree-synonym emphasis-1 #.(cl-ppcre::parse-string "_([^_]*)_")) (define-parse-tree-synonym emphasis-2 #.(cl-ppcre::parse-string "\\*([^ ][^\\*]*)\\*")) (define-parse-tree-synonym strong-1 (:sequence (:greedy-repetition 2 2 #\_) (:register (:sequence (:greedy-repetition 0 nil (:inverted-char-class #\_)))) (:greedy-repetition 2 2 #\_))) (define-parse-tree-synonym strong-2 (:sequence (:greedy-repetition 2 2 #\*) (:register (:sequence (:greedy-repetition 0 nil (:inverted-char-class #\*)))) (:greedy-repetition 2 2 #\*))) (define-parse-tree-synonym strong-em-1 (:sequence (:greedy-repetition 3 3 #\_) (:register (:sequence (:greedy-repetition 0 nil (:inverted-char-class #\_)))) (:greedy-repetition 3 3 #\_))) (define-parse-tree-synonym strong-em-2 (:sequence (:greedy-repetition 3 3 #\*) (:register (:sequence (:greedy-repetition 0 nil (:inverted-char-class #\*)))) (:greedy-repetition 3 3 #\*))) (define-parse-tree-synonym backtick #.(cl-ppcre::parse-string "\\`([^\\`]*)\\`")) (define-parse-tree-synonym auto-link #.(cl-ppcre::parse-string "<(http://[^>]*)>")) (define-parse-tree-synonym auto-mail #.(cl-ppcre::parse-string "<([^> ]*@[^> ]*)>")) (define-parse-tree-synonym html #.(cl-ppcre::parse-string "(\\<[^\\>]*\\>)")) (define-parse-tree-synonym entity #.(cl-ppcre::parse-string "(&[\\#a-zA-Z0-9]*;)")) (define-parse-tree-synonym hostname-char #.(cl-ppcre::parse-string "[-a-zA-Z0-9_.]")) (define-parse-tree-synonym hostname (:sequence (:greedy-repetition 1 nil hostname-char) (:greedy-repetition 0 nil (:sequence #\. (:greedy-repetition 1 nil hostname-char))))) (define-parse-tree-synonym pathname-char (:char-class #\- (:range #\a #\z) (:range #\A #\Z) (:range #\0 #\9) #\_ #\. #\: #\@ #\& #\? #\= #\+ #\, #\! #\/ #\~ #\* #\' #\% #\\ #\$ )) (define-parse-tree-synonym url-pathname (:sequence (:greedy-repetition 0 nil pathname-char))) (define-parse-tree-synonym url (:sequence "http://" (:register hostname) (:greedy-repetition 0 1 (:sequence (:greedy-repetition 0 1 #\/) (:register url-pathname (:greedy-repetition 0 1 (:sequence #\# url-pathname))) )) (:negative-lookbehind (:char-class #\. #\, #\? #\!)))) (define-parse-tree-synonym url-no-registers (:sequence (:greedy-repetition 0 1 (:sequence "http://" hostname)) (:greedy-repetition 0 1 (:sequence (:greedy-repetition 0 1 #\/) url-pathname)) (:greedy-repetition 0 1 (:sequence #\# url-pathname)) (:negative-lookbehind (:char-class #\. #\, #\? #\!)))) (define-parse-tree-synonym bracketed (:sequence #\[ (:register (:greedy-repetition 0 nil (:inverted-char-class #\[))) #\])) (defparameter *escape-characters* "\\`*_{}[]()#.!<>") ;; FIXME - use *escape-characters* to create this parse-tree (define-parse-tree-synonym valid-escape (:alternation #\\ ;backslash #\` ;backtick #\* ;asterisk #\_ ;underscore #\{ ;curly braces #\} #\[ ;square brackets #\] #\( ;parentheses #\) #\# ;hash mark #\. ;dot #\! ;exclamation mark #\< ;brackets #\> )) (define-parse-tree-synonym escaped-character (:sequence #\\ (:register valid-escape))) (define-parse-tree-synonym escape-kludge (:sequence #\Null #\Null (:register (:greedy-repetition 0 nil (:char-class (:range #\0 #\9)))) #\Null #\Null)) (define-parse-tree-synonym link+title (:sequence #\( (:alternation (:sequence #\< (:register (:greedy-repetition 0 nil (:inverted-char-class #\) #\Space))) #\>) (:register (:greedy-repetition 0 nil (:inverted-char-class #\) #\Space)))) ; title (:greedy-repetition 0 1 (:sequence (:greedy-repetition 1 nil :whitespace-char-class) (:alternation #\' #\" #\() (:register (:greedy-repetition 0 nil :everything)) (:alternation #\' #\" #\)))) #\))) (define-parse-tree-synonym inline-link (:sequence bracketed link+title)) (define-parse-tree-synonym reference-link (:sequence bracketed (:greedy-repetition 0 1 :whitespace-char-class) bracketed)) (define-parse-tree-synonym link-label (:sequence :start-anchor (:greedy-repetition 0 3 :whitespace-char-class) bracketed #\: (:greedy-repetition 0 nil :whitespace-char-class) (:register url-no-registers) (:greedy-repetition 0 1 (:sequence (:greedy-repetition 1 nil :whitespace-char-class) (:greedy-repetition 0 1 (:register (:alternation (:sequence #\( (:greedy-repetition 0 nil :everything) #\)) (:sequence #\" (:greedy-repetition 0 nil :everything) #\")))) (:register (:greedy-repetition 0 nil :everything)))))) (define-parse-tree-synonym extended-link-label (:sequence :start-anchor ;;; [reference]> (:greedy-repetition 0 3 :whitespace-char-class) bracketed #\> (:greedy-repetition 0 nil :whitespace-char-class) ;;; name (:register (:greedy-repetition 0 nil (:inverted-char-class :whitespace-char-class))) (:register (:greedy-repetition 0 nil :everything)) :end-anchor)) (define-parse-tree-synonym coded-reference-link (:sequence #\` (:register (:sequence ;;; NO! ;; (:non-greedy-repetition 0 nil (:inverted-char-class #\` #\[)) ; bracket (:sequence #\[ (:greedy-repetition 0 nil (:inverted-char-class #\[)) #\]) ; space (:greedy-repetition 0 1 :whitespace-char-class) ; bracket (:sequence #\[ (:greedy-repetition 0 nil (:inverted-char-class #\[)) #\]) ;;; NO! ;; (:non-greedy-repetition 0 nil (:inverted-char-class #\` #\])) )) #\`)) (define-parse-tree-synonym bracketed (:sequence #\[ (:register (:greedy-repetition 0 nil (:inverted-char-class #\[))) #\])) ;;; image-link (define-parse-tree-synonym inline-image (:sequence #\! bracketed link+title)) ;;; image-link reference (define-parse-tree-synonym inline-image (:sequence #\! bracketed link+title)) (define-parse-tree-synonym reference-image (:sequence #\! bracketed (:greedy-repetition 0 1 :whitespace-char-class) bracketed)) cl-markdown-20101006-darcs/.boring0000644000175000017500000000115411453110206015160 0ustar xachxach# Boring file regexps: \.hi$ \.o$ \.o\.cmd$ # *.ko files aren't boring by default because they might # be Korean translations rather than kernel modules. # \.ko$ \.ko\.cmd$ \.mod\.c$ (^|/)\.tmp_versions($|/) (^|/)CVS($|/) (^|/)RCS($|/) ~$ #(^|/)\.[^/] (^|/)_darcs($|/) \.bak$ \.BAK$ \.orig$ (^|/)vssver\.scc$ \.swp$ (^|/)MT($|/) (^|/)\{arch\}($|/) (^|/).arch-ids($|/) (^|/), \.class$ \.prof$ (^|/)\.DS_Store$ (^|/)BitKeeper($|/) (^|/)ChangeSet($|/) (^|/)\.svn($|/) \.py[co]$ \# \.cvsignore$ (^|/)Thumbs\.db$ (^|/)autom4te\.cache($|/) (^|/)scratch($|/) (^|/)two words($|/) (^|/)test-results($|/) \.dribble (^|/)make($|/) cl-markdown-20101006-darcs/resources/0000755000175000017500000000000011453110206015710 5ustar xachxachcl-markdown-20101006-darcs/resources/markdown-report-styles.css0000644000175000017500000000111411453110206023073 0ustar xachxachbody { font-family: Georgia, "Times New Roman", Times, serif; margin-right: 0.5in; margin-left: 0.5in; margin-bottom: 0.25px; } .markdown-report { margin-left: 4em; } #main-content { float: left; width: 80%; margin-bottom: 2em; line-height: 1.4; font-size: 80%; } #footer { margin-top: 2em; margin-bottom: 2em; padding-top: 0.25em; border-top-style: inset; border-top-width: 3px; clear: both; width: 80%; } div.markdown-report h1,h2 { margin-left: -2em; clear: both; margin-bottom: 0.5em; } .markdown-report span { float: left; display: block; width: 2.5in; }cl-markdown-20101006-darcs/website/0000755000175000017500000000000011453110206015340 5ustar xachxachcl-markdown-20101006-darcs/website/website.tmproj0000644000175000017500000001063511453110206020244 0ustar xachxach currentDocument source/resources/ug-footer.md documents name images regexFolderFilter !.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$ sourceDirectory source/images filename source/index.md lastUsed 2007-12-13T22:11:58Z expanded name resources regexFolderFilter !.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$ sourceDirectory source/resources filename source/style.css lastUsed 2007-11-21T15:32:54Z filename source/user-guide.css lastUsed 2007-12-13T22:11:59Z filename source/user-guide.md lastUsed 2007-12-13T23:24:58Z fileHierarchyDrawerWidth 200 metaData source/index.md caret column 0 line 82 columnSelection firstVisibleColumn 0 firstVisibleLine 53 selectFrom column 0 line 78 selectTo column 0 line 82 source/resources/footer.md caret column 6 line 10 firstVisibleColumn 0 firstVisibleLine 0 source/resources/header.md caret column 0 line 5 firstVisibleColumn 0 firstVisibleLine 0 source/resources/ug-footer.md caret column 6 line 9 firstVisibleColumn 0 firstVisibleLine 0 source/resources/ug-header.md caret column 7 line 8 firstVisibleColumn 0 firstVisibleLine 0 source/resources/ug-navigation.md caret column 0 line 0 firstVisibleColumn 0 firstVisibleLine 0 source/user-guide.css caret column 0 line 0 firstVisibleColumn 0 firstVisibleLine 0 openDocuments source/resources/ug-footer.md source/resources/footer.md source/index.md source/user-guide.css source/resources/header.md source/resources/ug-header.md source/resources/ug-navigation.md showFileHierarchyDrawer windowFrame {{161, 22}, {684, 791}} cl-markdown-20101006-darcs/website/source/0000755000175000017500000000000011453110206016640 5ustar xachxachcl-markdown-20101006-darcs/website/source/images/0000755000175000017500000000000011453110206020105 5ustar xachxachcl-markdown-20101006-darcs/website/source/user-guide.css0000644000175000017500000000324211453110206021424 0ustar xachxach/* @override file:///Users/gwking/darcs/log5/website/output/style.css */ pre { background-color:#e0e0e0; overflow: auto; margin-right: 1em; margin-left: 1em; padding: 5px; } body { background: white; margin: 2px; padding-left: 6em; padding-right: 6em; } .note { border: 2px inset gray; padding: 0.5em; margin-right: 2em; margin-left: 2em; } #footer { margin-top: 2em; border-top-style: inset; border-top-width: 2px; } #header { text-align: left; border-top: 1px none black; border-collapse: collapse; border-bottom: 1px dotted black; margin-bottom: 1em; } #navigation li { display: inline; border-right-style: dotted; border-right-width: 1px; border-left-style: dotted; border-left-width: 1px; border-collapse: collapse; padding-right: 0.25em; padding-left: 0.25em; margin-right: 1em; } #navigation { text-align: center; } #timestamp { font-size: 80%; text-align: right; } a.none { text-decoration: none; color:black } a.none:visited { text-decoration: none; color:black } a.none:active { text-decoration: none; color:black } a.none:hover { text-decoration: none; color:black } a { text-decoration: none; } a:visited { text-decoration: none; } a:active { text-decoration: underline; } a:hover { text-decoration: underline; } .note { } .windows { } /* @group toc */ .table-of-contents { font-size: 90%; } .table-of-contents h1, h2, h3, h4, h5, h6, h7 { font-size: inherit; margin-top: -0.5em; } .table-of-contents h2 { position: relative; left: 2em; } .table-of-contents h3 { position: relative; left: 4em; } .table-of-contents h4 { position: relative; left: 6em; } .table-of-contents h5 { position: relative; left: 8px; } /* @end */ cl-markdown-20101006-darcs/website/source/index.md0000644000175000017500000001062111453110206020271 0ustar xachxach{include resources/header.md} {set-property title "CL-Markdown - Markdown and More"}
    ### What it is (Note: CL-Markdown just split off it's Lisp documentation abilities into the [docudown][] project. Don't be alarmed. Everything is good.) [Markdown][] is [John Gruber][df]'s text markup langauge and the Perl program that converts documents written in that language into HTML. CL-Markdown is a Common Lisp rewrite of Markdown. CL-Markdown is licensed under the [MIT license][mit-license]. You can see the source of this page by clicking in the address bar of your browser and changing the extension from `html` to `text`. For example, this page's source is at [index.text](index.text). You can view a comparison of Markdown and CL-Markdown output [here][8]. {anchor mailing-lists} ### Mailing Lists * [devel-list][]: A list for questions, patches, bug reports, and so on; It's for everything other than announcements. {anchor downloads} ### Where is it The easiest way to get setup with CL-Markdown is by using [ASDF-Install][14]. If that doesn't float your boat, there is a handy [gzipped tar file][15] and a [Darcs][16] repository. The darcs commands to retrieve the CL-Markdown source is: darcs get "http://common-lisp.net/project/cl-markdown" (note that this won't let you build CL-Markdown unless you also get all of its dependencies...) {anchor news} ### What is happening
    28 May 2008
    Many small improvements, bug fixes, tweaks, and extensions. The biggest change, however, is that I've move the Lisp documentation work into it's own [project][docudown]. This keeps CL-Markdown simpler. The dependencies on [moptilities][] and [defsystem-compatibility][] have both been removed. A dependency on [anaphora][clnet-anaphora] has been added.
    30 August 2007
    Tons of improvements in the documentation extension, lots of cleanup, better HTML generation, better footnotes, what's not to like!
    20 Feb 2007
    Lots of stuff has happened; see the change log for details.
    5 June 2006
    More tweaking of block structure processing and paragraph marking. In every day and in every way, it's getting better and better.
    22 May 2006
    Removed LML2 dependency for CL-Markdown and fixed some bugs!
    17 May 2006
    Updated with SBCL and Allegro support (son far only alisp)
    8 May 2006
    Created site.
    {include resources/footer.md} [1]: http://common-lisp.net/project/cl-containers/shared/metabang-2.png (metabang.com) [2]: http://www.metabang.com/ (metabang.com) [3]: #mailing-lists [4]: #downloads [5]: documentation/ (documentation link) [6]: #news [7]: changelog.html [8]: comparison-tests [9]: http://trac.common-lisp.net/cl-markdown [10]: http://trac.common-lisp.net/cl-markdown/newticket [11]: http://common-lisp.net/cgi-bin/mailman/listinfo/cl-markdown-announce [12]: http://common-lisp.net/cgi-bin/mailman/listinfo/cl-markdown-devel [13]: downloads [14]: http://www.cliki.net/asdf-install [15]: http://common-lisp.net/project/cl-markdown/cl-markdown_latest.tar.gz [16]: http://www.darcs.net/ [17]: news [18]: http://common-lisp.net/project/cl-containers/shared/buttons/xhtml.gif (valid xhtml button) [19]: http://validator.w3.org/check/referer (xhtml1.1) [20]: http://common-lisp.net/project/cl-containers/shared/buttons/hacker.png (hacker emblem) [21]: http://www.catb.org/hacker-emblem/ (hacker) [22]: http://common-lisp.net/project/cl-containers/shared/buttons/lml2-powered.png (lml2 powered) [23]: http://lml2.b9.com/ (lml2 powered) [24]: http://common-lisp.net/project/cl-containers/shared/buttons/lambda-lisp.png (ALU emblem) [25]: http://www.lisp.org/ (Association of Lisp Users) [26]: http://common-lisp.net/project/cl-containers/shared/buttons/lisp-lizard.png (Common-Lisp.net) [27]: http://common-lisp.net/ (Common-Lisp.net) cl-markdown-20101006-darcs/website/source/resources/0000755000175000017500000000000011453110206020652 5ustar xachxachcl-markdown-20101006-darcs/website/source/resources/ug-header.md0000644000175000017500000000065611453110206023044 0ustar xachxach{set-property html yes} {set-property style-sheet user-guide} [darcs]: http://www.darcs.net/ [asdf-install]: http://common-lisp.net/project/asdf-install [tarball]: http://common-lisp.net/project/cl-markdown/cl-markdown_latest.tar.gz [gwking]: http://www.metabang.com/ [cl-markdown-cliki]: http://www.cliki.net/cl-markdown [user-guide]: user-guide.html cl-markdown-20101006-darcs/website/source/resources/ug-navigation.md0000644000175000017500000000007011453110206023741 0ustar xachxach cl-markdown-20101006-darcs/website/source/resources/footer.md0000644000175000017500000000276011453110206022477 0ustar xachxachcl-markdown-20101006-darcs/website/source/resources/ug-footer.md0000644000175000017500000000035111453110206023102 0ustar xachxach cl-markdown-20101006-darcs/website/source/resources/navigation.md0000644000175000017500000000003511453110206023331 0ustar xachxach cl-markdown-20101006-darcs/website/source/resources/header.md0000644000175000017500000000130711453110206022425 0ustar xachxach{include shared-links.md} {set-property html yes} {set-property style-sheet "http://common-lisp.net/project/cl-containers/shared/style.css"} {set-property author "Gary Warren King"} [devel-list]: http://common-lisp.net/cgi-bin/mailman/listinfo/cl-markdown-devel [cliki-home]: http://www.cliki.net/cl-markdown [tarball]: http://common-lisp.net/project/cl-markdown/cl-markdown.tar.gz
    ## CL-Markdown #### Finally, text mucking fun the whole family can enjoy
    cl-markdown-20101006-darcs/website/source/style.css0000644000175000017500000000303311453110206020511 0ustar xachxachpre { padding:5px; background-color:#e0e0e0; overflow: auto; } body { background: white; margin: 2px; padding-left: 3em; padding-right: 3em; } .note { border: 2px inset gray; padding: 0.5em; margin-right: 2em; margin-left: 2em; } #footer { margin-top: 2em; border-top-style: inset; border-top-width: 2px; } #header { text-align: left; border-top: 1px none black; border-collapse: collapse; border-bottom: 1px dotted black; margin-bottom: 1em; } #navigation li { display: inline; border-right-style: dotted; border-right-width: 1px; border-left-style: dotted; border-left-width: 1px; border-collapse: collapse; padding-right: 0.25em; padding-left: 0.25em; margin-right: 1em; } #navigation { text-align: center; } #timestamp { font-size: 80%; text-align: right; } a.none { text-decoration: none; color:black } a.none:visited { text-decoration: none; color:black } a.none:active { text-decoration: none; color:black } a.none:hover { text-decoration: none; color:black } a { text-decoration: none; } a:visited { text-decoration: none; } a:active { text-decoration: underline; } a:hover { text-decoration: underline; } .note { } .windows { } /* @group toc */ .table-of-contents { font-size: 90%; } .table-of-contents h1, h2, h3, h4, h5, h6, h7 { font-size: inherit; } .table-of-contents h2 { position: relative; left: 2em; } .table-of-contents h3 { position: relative; left: 4em; } .table-of-contents h4 { position: relative; left: 6em; } .table-of-contents h5 { position: relative; left: 8px; } /* @end */ cl-markdown-20101006-darcs/website/source/user-guide.md0000644000175000017500000001460011453110206021234 0ustar xachxach{include resources/ug-header.md} {set-property title "CL-Markdown User's Guide"} # CL-Markdown - Quick Start {table-of-contents :start 2 :depth 3} CL-Markdown is an enhanced version of John Gruber's [Markdown][] text markup langauge. Markdown's goal is to keep text readable as *text* and as HTML. CL-Markdown keeps this principle and adds a flexible extension mechanism so that you can build complex documents easily. [Markdown]: http://daringfireball.net/projects/markdown/ ### Getting Started The easiest way to install CL-Markdown is using the [bundle][]. You can also use [ASDF-Install][], download tarballs or grab the sources directly (usings [darcs][]). If you do use the bundle, here is what you'd do: shell> cd shell> curl http://common-lisp.net/project/cl-markdown/cl-markdown-bundle.tar.gz > cl-markdown-bundle.tar.gz shell> tar -zxvf cl-markdown-bundle.tar.gz shell> lisp ;; Super Lisp 5.3 (just kidding) lisp: (require 'asdf) lisp: (load "cl-markdown-bundle/cl-markdown.asd") lisp: (asdf:oos 'asdf:load-op 'cl-markdown) lisp: (in-package cl-markdown) The top-level CL-Markdown command is `markdown`. It creates a `document` from a source (pathname, stream or string) and then sends the document to a stream in a `format`. The default format is `:html` and the default output is `t` (which sends the output to `*standard-output*`.). You can use an already open stream for output, provide a pathname to a file (which will be overwritten!) or use the symbol `nil` to direct output to a new stream. At this time, support for formats other than HTML is not provided. For example: lisp: (markdown "# Hello *there*") "

    Hello there

    " CL-Markdown implements most of John Gruber's [specification][markdown-specification] (though it does not yet handle e-mails and some edges cases). It also adds a new syntax for extensions. [markdown-specification]: http://daringfireball.net/projects/markdown/syntax ### Function calls: \{ and \} Calling extension functions requires three things: 1. writing (or finding) the extension that you want 2. telling CL-Markdown that you want to use the extension 3. writing your Markdown text with calls to the extension The last part is the easiest; all you need to do is open a curly brace, type the name of extension function, type in the arguments (separated by spaces) and type a closing curly brace. For example: "\{now\}" will generate the text "{now}". The second step is necessary because CL-Markdown won't recognize functions as functions unless you tell it to up front. After all, you wouldn't want to allow people to execute arbitrary code; it **might** be a security risk (smile). Because CL-Markdown operates in two stages, there are two times when functions can be called: during parsing and during rendering. Functions active during these stages are keep in the special variables `*render-active-functions*` and `*parse-active-functions*`. An example might make this clearer. First, we'll call Markdown without specifying any functions: ? (markdown "Today is {today}. It is {now}." :format :html :stream t)

    Today is ; Warning: Inactive or undefined CL-Markdown function TODAY ; While executing: # . It is ; Warning: Inactive or undefined CL-Markdown function NOW ; While executing: # .

    As you can see, the functions weren't ones that CL-Markdown was ready to recognize, so we got warnings and no text was generated. If we tell CL-Markdown that `today` and `now` should be treated as functions, then we see a far prettier picture: ? (let ((*render-active-functions* (append '(today now) *render-active-functions*))) (markdown "Today is {today}. It is {now}." :format :html :stream t))

    Today is 1 August 2006. It is 11:36.

    By now, we've seen how to include function calls in CL-Markdown documents and how to generate those documents with CL-Markdown. The final piece of the puzzle is actually writing the extensions. #### Writing Cl-Markdown extensions There are several ways to write extensions. {footnote Extensions beg for a little {abbrev DSL Domain Specific Language} but those macros are still to be written.} The easiest is one is to write functions active during rendering that return the text that you wish to be included in the document. For example: (defun today (phase arguments result) (declare (ignore phase arguments result)) (format-date "%e %B %Y" (get-universal-time))) The format-date command is part of [metatilities-base][]; it returns a string of the date using the C library inspired date format. This string is placed in the document whereever the function call (\{today\}) is found. [metatilities]: Alternately, one can use the `*output-stream*` variable to insert more complicated text. This would look like: (defun now (phase arguments result) (declare (ignore phase arguments result)) (format *output-stream* "~a" (format-date "%H:%M" (get-universal-time))) nil) (Note that `now` returns `nil` so that the date isn't inserted twice!). The other alternative is to use your function calls to alter the structure of the CL-Markdown document and then let Markdown deal with some or all of the rest. The `anchor` extension provides an example of this: (defun anchor (phase &rest args) (ecase phase (:parse (let ((name (caar args)) (title (cadar args))) (setf (item-at (link-info *current-document*) name) (make-instance 'link-info :id name :url (format nil "#~a" name) :title (or title ""))))) (:render (let ((name (caar args))) (format nil "" name name))))) `Anchor` makes it easier to insert anchors into your document and to link to those anchors from elsewhere. It is active during both parsing and rendering. During the parsing phase, it uses it's arguments to determine the name and title of the link and places this into the current document's link information table. During rendering, it outputs the HTML needed to mark the link. {footnote If you would like to see more examples, look in the files `extensions.lisp` or `footnotes.lisp`.}
    {footnotes} {include resources/ug-footer.md} cl-markdown-20101006-darcs/cl-markdown.asd0000644000175000017500000000413011453110206016603 0ustar xachxach(in-package #:common-lisp-user) (defpackage #:cl-markdown-system (:use #:cl #:asdf)) (in-package #:cl-markdown-system) (defsystem cl-markdown :version "0.10.4" :author "Gary Warren King " :maintainer "Gary Warren King " :licence "MIT Style License" :components ((:static-file "COPYING") (:module "setup" :pathname "dev/" :components ((:file "package") (:file "api" :depends-on ("package")))) (:module "dev" :depends-on ("setup") :components ((:file "definitions") (:file "macros") (:file "class-defs" :depends-on ("definitions")) (:file "utilities" :depends-on ("macros" "definitions" "class-defs")) (:file "spans" :depends-on ("regexes" "class-defs")) (:file "regexes") (:file "markdown" :depends-on ("utilities" "class-defs" "spans" "definitions")) (:file "html" :depends-on ("utilities" "class-defs" "spans")) (:file "plain" :depends-on ("utilities" "class-defs" "spans")) (:file "multiple-documents" :depends-on ("definitions")) (:file "epilogue" :depends-on ("markdown")) (:static-file "notes.text"))) (:module "extensions" :pathname #.(make-pathname :directory '(:relative "dev")) :components ((:file "extension-mechanisms") (:file "extensions" :depends-on ("extension-mechanisms")) (:file "footnotes" :depends-on ("extension-mechanisms"))) :depends-on ("dev")) (:module "website" :components ((:module "source" :components ((:static-file "index.md")))))) :in-order-to ((test-op (load-op cl-markdown-test))) :perform (test-op :after (op c) (funcall (intern (symbol-name '#:run-tests) :lift) :config :generic)) :depends-on ((:version :metatilities-base "0.6.0") :metabang-bind :dynamic-classes :cl-containers :anaphora :cl-ppcre)) (defmethod operation-done-p ((o test-op) (c (eql (find-system '#:cl-markdown)))) (values nil)) cl-markdown-20101006-darcs/lift-standard.config0000644000175000017500000000140211453110206017616 0ustar xachxach;;; configuration for LIFT tests ;; settings (:if-dribble-exists :supersede) (:dribble "lift.dribble") (:print-length 10) (:print-level 5) (:print-test-case-names t) ;; suites to run (cl-markdown-test) ;; report properties (:report-property :title "CL-Markdown | Test results") (:report-property :relative-to cl-markdown-test) (:report-property :style-sheet "test-style.css") (:report-property :if-exists :supersede) (:report-property :format :html) (:report-property :name "test-results/test-report") (:report-property :unique-name t) (:build-report) (:report-property :format :save) (:report-property :name "test-results/test-report.sav") (:build-report) (:report-property :format :describe) (:report-property :full-pathname *standard-output*) (:build-report) cl-markdown-20101006-darcs/unit-tests/0000755000175000017500000000000011453110206016015 5ustar xachxachcl-markdown-20101006-darcs/unit-tests/comparison.lisp0000644000175000017500000002654511453110206021074 0ustar xachxach(in-package #:cl-markdown-test) #| source .text cl-markdown .html tidy .xxxx markdown .down tidy .mark |# #+(or) (cl-markdown-test::compare-all) #+(or) (compare-markdown-and-cl-markdown (pathname-name (first (directory (make-pathname :name :wild :type "text" :defaults *test-source-directory*))))) #+(or) (compare-markdown-and-cl-markdown "Auto Links") (defvar *errors* nil) (defvar *all-wells* nil) (defvar *data* nil "What a hack! Shoot me") (defparameter *test-source-directory* (system-relative-pathname 'cl-markdown (make-pathname :directory '(:relative "unit-tests" "markdown-tests")))) (defparameter *test-output-directory* (system-relative-pathname 'cl-markdown (make-pathname :directory '(:relative "website" "output" "comparison-tests")))) (defun compare-markdown-and-cl-markdown (basename) (cl-markdown-and-tidy basename) (markdown-and-tidy basename) (create-comparison-file basename)) (defun compare-all () (setf *errors* nil *all-wells* nil) (iterate-elements (directory (make-pathname :name :wild :type "text" :defaults *test-source-directory*)) (lambda (file) (handler-case (compare-markdown-and-cl-markdown (pathname-name file)) (error (c) (push (pathname-name file) *errors*) (create-error-file (pathname-name file) c))))) (create-main-comparison-page) (copy-file (make-pathname :type "css" :name "style" :defaults *test-source-directory*) (make-pathname :type "css" :name "style" :defaults *test-output-directory*) :if-exists :supersede)) (defun create-main-comparison-page () (let ((output (make-pathname :type "html" :name "index" :defaults *test-output-directory*))) (ensure-directories-exist output) (with-new-file (s output) (lml2:html-stream s (lml2:html (:head (:title "Index | CL-Markdown / Markdown Comparison") ((:link :rel "stylesheet" :href "style.css"))) (:body ((:div :id "contents") (:p "Below are the results of running " ((:a :href "http://www.common-lisp.net/project/cl-markdown") "CL-Markdown") " and the Perl " ((:a :href "http://www.daringfireball.net/markdown") "Markdown") " script on the same input. You'll see that the current version of CL-Markdown performs well on most documents and poorly on a few. You'll also find that the rendered HTML can be very similar even where the diffs between outputs contains many insertions and deletions.") (:p "This will be updated regularly. The most recent update was " (lml2:lml-princ (format-date "%e %B %Y" (get-universal-time)))) (:h2 "Comparison Tests") (iterate-elements (directory (make-pathname :name :wild :type "text" :defaults *test-source-directory*)) (lambda (file) (bind ((entry-file (comparison-file-name (pathname-name file))) (entry (namestring (make-pathname :name (pathname-name entry-file) :type "html"))) (data (find (pathname-name file) *data* :test #'string-equal :key #'car)) ((nil replace insert delete) (or data (list nil nil nil nil)))) (lml2:html ((:span :class (cond ((find (pathname-name file) *errors* :test #'string-equal) "index-entry error") ((find (pathname-name file) *all-wells* :test #'string-equal) "index-entry good") (t "index-entry"))) ((:a :href entry) (lml2:lml-princ entry) (unless (and (and replace (zerop replace)) (and delete (zerop delete)) (and insert (zerop insert))) (lml2:lml-format " (~D, ~D, ~D)" replace delete insert)))))))) ((:div :id "notes") (:p "In the rare case that CL-Markdown produces invalid HTML. Most browsers will still display the output but " ((:a :href "tidy") "Tidy") " reports errors and produces no output. This will show up as a blank section on the comparison page. As far as I know, the HTML CL-Markdown is now always valid.") (:p "Files with this " ((:span :class "error") "color") " had Lisp errors during the run. " "Files with this " ((:span :class "good") "color") " had no differences from Markdown output during the run." "The numbers in parentheses represent the number of replacements, inserts, and deletes that occurred during the diff.")) ((:div :id "footer") "end 'o page")))))))) (defun cl-markdown-and-tidy (basename) (let* ((inpath (make-pathname :type "text" :name basename :defaults *test-source-directory*)) (output (make-pathname :type "html" :name basename :defaults *test-source-directory*))) (markdown inpath :format :html :stream output) (tidy basename "html" "xxxx") output)) (defun create-error-file (basename condition) (let ((output (comparison-file-name basename))) (ensure-directories-exist output) (with-new-file (s output) (lml2:html-stream s (lml2:html (:head (:title "CL-Markdown / Markdown Comparison") ((:link :rel "stylesheet" :href "style.css"))) (:body ((:div :id "contents") (:p "Error during parsing of '" (lml2:lml-princ basename) "'.") ((:a :href "index.html") "Back to index") (:p (:pre (lml2:lml-princ (html-encode:encode-for-pre (html-encode:encode-for-http (format nil "~A" condition)))))) (:div ((:div :id "original-source") (:h1 "Original source") ((:div :class "section-contents") (:pre (lml2:lml-princ (html-encode:encode-for-pre (file->string (make-pathname :type "text" :name basename :defaults *test-source-directory*)))))))) ((:div :id "footer") "end 'o page")))))))) (defun markdown-and-tidy (basename) (let* ((inpath (make-pathname :type "text" :name basename :defaults *test-source-directory*)) (outpath (make-pathname :type "mark" :name basename :defaults *test-source-directory*))) (metashell:shell-command (format nil "/usr/local/bin/markdown '~a' > '~A'" (system-namestring inpath) (system-namestring outpath))) (tidy basename "mark" "down") outpath)) (defun tidy (basename input-type output-type) (let* ((inpath (make-pathname :type input-type :name basename :defaults *test-source-directory*)) (tidy-output (make-pathname :type output-type :name basename :defaults *test-source-directory*)) (command (format nil "/usr/bin/tidy --show-body-only 1 --quiet 1 --show-warnings 0 '~A' > '~A'" (system-namestring inpath) (system-namestring tidy-output)))) (metashell:shell-command command) (when (zerop (kl:file-size tidy-output)) ;; an error in the HTML (error "HTML Error for ~A" basename)) tidy-output)) (defun comparison-file-name (basename) (make-pathname :defaults *test-output-directory* :type "html" :name (concatenate 'string basename "-compare"))) (defun create-comparison-file (basename) (bind ((cl-file (make-pathname :type "xxxx" :name basename :defaults *test-source-directory*)) (md-file (make-pathname :type "down" :name basename :defaults *test-source-directory*)) ((values diff replace insert delete) (html-diff::html-diff (file->string md-file) (file->string cl-file))) (output (comparison-file-name basename))) (push (list basename replace insert delete) *data*) (ensure-directories-exist output) (with-new-file (s output) (lml2:html-stream s (lml2:html (:head (:title "CL-Markdown / Markdown Comparison") ((:link :rel "stylesheet" :href "style.css"))) (:body ((:div :id "contents") ((:div :id "header") (:h1 "File: " (lml2:lml-princ basename) ".text")) ((:a :href "index.html") "Back to index") (:div ((:div :id "cl-markdown-output") (:h1 "CL-Markdown") ((:div :class "section-contents") (lml2:insert-file cl-file))) ((:div :id "markdown-output") (:h1 "Markdown") ((:div :class "section-contents") (lml2:insert-file md-file)))) (:div ((:div :id "diff-output") (:h1 "HTML Difference") ((:div :class "section-contents") (cond ((and (zerop insert) (zerop delete) (zerop replace)) (push basename *all-wells*) (lml2:lml-princ "No differences")) (t (lml2:html (:p "Insert: " (lml2:lml-princ insert) ", Delete: " (lml2:lml-princ delete) ", Replace " (lml2:lml-princ replace)) (lml2:lml-princ diff)))))) ((:div :id "cl-markdown-html") (:h1 "HTML from CL Markdown") ((:div :class "section-contents") (:pre (lml2:lml-princ (html-encode:encode-for-pre (html-encode:encode-for-http (file->string cl-file)))))))) (:div ((:div :id "original-source") (:h1 "Original source") ((:div :class "section-contents") (:pre (lml2:lml-princ (html-encode:encode-for-pre (html-encode:encode-for-http (file->string (make-pathname :type "text" :name basename :defaults *test-source-directory*))))))))) ((:div :id "footer") "end 'o page")))))))) (defun file->string (pathname) (apply 'concatenate 'string (with-iterator (iterator (make-pathname :defaults pathname) :treat-contents-as :lines :skip-empty-chunks? nil) (collect-elements iterator :transform (lambda (line) (format nil "~%~A" line)))))) cl-markdown-20101006-darcs/unit-tests/test-extensions.lisp0000644000175000017500000000215711453110206022067 0ustar xachxach(in-package #:cl-markdown-test) (deftestsuite test-extensions-basic (cl-markdown-test) ()) (deftestsuite test-properties (test-extensions-basic) ()) (addtest (test-properties) basic-get/set (ensure-same (cl-markdown::strip-whitespace (strip-html (nth-value 1 (markdown "{set-property _my-prop_ \"hello there\"} I say '{property _my-prop_}'." :stream nil)))) "I say 'hello there'.")) (addtest (test-properties) set/get-embedded-markdown (let ((markdown (nth-value 1 (markdown "{set-property version \"**3.1**\"} I say '{property version}'." :stream nil)))) (ensure (search "" markdown :test #'char=)) (ensure-same (cl-markdown::strip-whitespace (strip-html markdown)) "I say '3.1'."))) ;; fails because the embedded {} aren't properly parsed (addtest (test-properties) set/get-embedded-property (ensure-same (cl-markdown::strip-whitespace (strip-html (nth-value 1 (markdown "{set-property version \"3.1\"} {set-property version-name \"version-{version}\"} I say '{property version-name}'." :stream nil)))) "I say 'version-3.1'.")) cl-markdown-20101006-darcs/unit-tests/framework.lisp0000644000175000017500000000235411453110206020707 0ustar xachxach(in-package #:cl-markdown-test) ;;; from ASDF-Install #-:digitool (defun system-namestring (pathname) (namestring (truename pathname))) #+:digitool (defvar *start-up-volume* (second (pathname-directory (truename "ccl:")))) #+:digitool (defun system-namestring (pathname) ;; this tries to adjust the root directory to eliminate the spurious ;; volume name for the boot file system; it also avoids use of ;; TRUENAME as some applications are for not yet existent files (let ((truename (probe-file pathname))) (unless truename (setf truename (translate-logical-pathname (merge-pathnames pathname *default-pathname-defaults*)))) (let ((directory (pathname-directory truename))) (flet ((string-or-nil (value) (when (stringp value) value)) (absolute-p (directory) (eq (first directory) :absolute)) (root-volume-p (directory) (equal *start-up-volume* (second directory)))) (format nil "~:[~;/~]~{~a/~}~@[~a~]~@[.~a~]" (absolute-p directory) (if (root-volume-p directory) (cddr directory) (cdr directory)) (string-or-nil (pathname-name truename)) (string-or-nil (pathname-type truename))))))) cl-markdown-20101006-darcs/unit-tests/brackets-with-empty-lines.lisp0000644000175000017500000000203611453110206023722 0ustar xachxach(in-package #:cl-markdown-test) (deftestsuite brackets-with-empty-lines (test-bracket-processing) ()) (addtest (brackets-with-empty-lines) linefeed-in-bracket (ensure (search "guide for test 3.0" (nth-value 1 (markdown "{set-property test \"3.0\"} This is the guide for test {property test}. It rocks." :stream nil)) :test 'char=))) (addtest (brackets-with-empty-lines) no-linefeeds (ensure (search "footnoteBacklink" (nth-value 1 (markdown " Hi there this is a footnote{footnote \"Actualy, this is\"}. Nice. {footnotes}" :stream nil)) :test #'char=))) (addtest (brackets-with-empty-lines) one-linefeed (ensure (search "footnoteBacklink" (nth-value 1 (markdown " Hi there this is a footnote{footnote \"Actualy, this is\"}. Nice. {footnotes}" :stream nil)) :test #'char=))) (addtest (brackets-with-empty-lines) two-linefeeds (ensure (search "footnoteBacklink" (nth-value 1 (markdown " Hi there this is a footnote{footnote \"Actualy, this is\"}. Nice. {footnotes}" :stream nil)) :test #'char=))) cl-markdown-20101006-darcs/unit-tests/test-utilities.lisp0000644000175000017500000000454511453110206021706 0ustar xachxach(in-package #:cl-markdown-test) (deftestsuite test-utilities (cl-markdown-test-all) ()) (deftestsuite list-depth (test-utilities) () (:test ((ensure-same (list-depth '(((a)))) 3))) (:test ((ensure-same (list-depth '(((a) b))) 3))) (:test ((ensure-same (list-depth '(((a b)))) 3))) (:test ((ensure-same (list-depth '(a)) 1))) (:test ((ensure-same (list-depth nil) 0)))) (deftestsuite test-list->tree (test-utilities) () (:test ((ensure-same (list->tree '((a 1) (b 1) (c 1)) :key #'first :depth-fn #'second) '(a b c) :test 'equal))) (:test ((ensure-same (list->tree '((a 1) (b 1) (c 2)) :key #'first :depth-fn #'second) '(a b (c)) :test 'equal))) (:test ((ensure-same (list->tree '((a 2) (b 1) (c 1)) :key #'first :depth-fn #'second) '((a) b c) :test 'equal))) (:test ((ensure-same (list->tree '((a 1) (b 2) (c 1)) :key #'first :depth-fn #'second) '(a (b) c) :test 'equal))) (:test ((ensure-same (list->tree '((a 1) (b 2) (c 3)) :key #'first :depth-fn #'second) '(a (b (c))) :test 'equal))) (:test ((ensure-same (list->tree '((a 1) (b 2) (c 3) (d 3)) :key #'first :depth-fn #'second) '(a (b (c d))) :test 'equal))) (:test ((ensure-same (list->tree '((a 3) (b 2) (c 1)) :key #'first :depth-fn #'second) '(((a) b) c) :test 'equal))) (:test ((ensure-same (list->tree '((a 1) (b 2) (c 3) (d 3) (e 1)) :key #'first :depth-fn #'second) '(a (b (c d)) e) :test 'equal))) (:test ((ensure-same (list->tree '((a 1) (b 2 :q) (c 3 :q) (d 3) (e 1)) :key #'first :depth-fn #'second :marker #'third) '(a (:q b (:q c d)) e) :test 'equal)))) (deftestsuite test-merge-atom-with-list-at-depth (test-utilities) ()) (addtest (ensure-same (merge-atom-with-list-at-depth 'a '((b) c) 2) '((a b) c) :test 'tree-equal)) (addtest (ensure-same (merge-atom-with-list-at-depth 'a '((b) c) 3) '(((a)) (b) c) :test 'tree-equal)) (addtest (ensure-same (merge-atom-with-list-at-depth 'a '((b) c) 1) '(a (b) c) :test 'tree-equal)) (addtest (ensure-same (merge-atom-with-list-at-depth 'b '(((c d)) e) 2) '((b (c d)) e) :test 'tree-equal)) cl-markdown-20101006-darcs/unit-tests/markdown-tests/0000755000175000017500000000000011453110206020777 5ustar xachxachcl-markdown-20101006-darcs/unit-tests/markdown-tests/bullets-and-numbers-1.text0000644000175000017500000000022311453110206025723 0ustar xachxachFirst **paragraph** Second Third * bullet 1 1. Is __it__ true? * what it [is][why] Last paragraph [why]: http://www.butter.com/ cl-markdown-20101006-darcs/unit-tests/markdown-tests/Tabs.text0000644000175000017500000000046711453110206022605 0ustar xachxach+ this is a list item indented with tabs + this is a list item indented with spaces Code: this code block is indented by one tab And: this code block is indented by two tabs And: + this is an example list item indented with tabs + this is an example list item indented with spaces cl-markdown-20101006-darcs/unit-tests/markdown-tests/Auto links.text0000644000175000017500000000040711453110206023717 0ustar xachxachLink: . With an ampersand: * In a list? * * It should. > Blockquoted: Auto-links should not occur here: `` or here: cl-markdown-20101006-darcs/unit-tests/markdown-tests/Literal quotes in titles.text0000644000175000017500000000015411453110206026456 0ustar xachxachFoo [bar][]. Foo [bar](/url/ "Title with "quotes" inside"). [bar]: /url/ "Title with "quotes" inside" cl-markdown-20101006-darcs/unit-tests/markdown-tests/Links, inline style.text0000644000175000017500000000025611453110206025424 0ustar xachxachJust a [URL](/url/). [URL and title](/url/ "title"). [URL and title](/url/ "title preceded by two spaces"). [URL and title](/url/ "title preceded by a tab"). [Empty](). cl-markdown-20101006-darcs/unit-tests/markdown-tests/style.css0000644000175000017500000000211111453110206022644 0ustar xachxach body { margin: 2em; background: #000033; } #contents { margin: 002em; background: #9999cc; padding: 1em; border: 3px outset #9999cc; } h1 { font-size: 120%; } #footer { clear: both; padding-top: 2em; text-align: left; font-style: italic; } .section-contents { margin-left: 2em; background: #ccccff; padding: 0.5em; } .diff { } ins { color: green; } del { color: red; text-decoration: line-through; } #cl-markdown-output { float: left; width: 50%; overflow: auto; } #notes { font-size: 80%; clear: both; padding-top: 1em; padding-right: 1em; padding-left: 1em; } #markdown-output { float: left; overflow: auto; width: 50%; } #diff-output { float: left; clear: both; overflow: auto; } #cl-markdown-html { float: left; overflow: auto; } #original-source { float: left; clear: both; overflow: auto; } .index-entry { float: left; clear: both; text-decoration: none; line-height: 21pt; } .index-entry a { text-decoration: none; } .error a { color: red; } .error { color: red; } .good a { color: #330099; } .good { color: #330099; } cl-markdown-20101006-darcs/unit-tests/markdown-tests/Tidyness.text0000644000175000017500000000011611453110206023505 0ustar xachxach> A list within a blockquote: > > * asterisk 1 > * asterisk 2 > * asterisk 3 cl-markdown-20101006-darcs/unit-tests/markdown-tests/Markdown Documentation - Syntax.text0000644000175000017500000006544711453110206027665 0ustar xachxachMarkdown: Syntax ================ * [Overview](#overview) * [Philosophy](#philosophy) * [Inline HTML](#html) * [Automatic Escaping for Special Characters](#autoescape) * [Block Elements](#block) * [Paragraphs and Line Breaks](#p) * [Headers](#header) * [Blockquotes](#blockquote) * [Lists](#list) * [Code Blocks](#precode) * [Horizontal Rules](#hr) * [Span Elements](#span) * [Links](#link) * [Emphasis](#em) * [Code](#code) * [Images](#img) * [Miscellaneous](#misc) * [Backslash Escapes](#backslash) * [Automatic Links](#autolink) **Note:** This document is itself written using Markdown; you can [see the source for it by adding '.text' to the URL][src]. [src]: /projects/markdown/syntax.text * * *

    Overview

    Philosophy

    Markdown is intended to be as easy-to-read and easy-to-write as is feasible. Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it's been marked up with tags or formatting instructions. While Markdown's syntax has been influenced by several existing text-to-HTML filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4], [Grutatext] [5], and [EtText] [6] -- the single biggest source of inspiration for Markdown's syntax is the format of plain text email. [1]: http://docutils.sourceforge.net/mirror/setext.html [2]: http://www.aaronsw.com/2002/atx/ [3]: http://textism.com/tools/textile/ [4]: http://docutils.sourceforge.net/rst.html [5]: http://www.triptico.com/software/grutatxt.html [6]: http://ettext.taint.org/doc/ To this end, Markdown's syntax is comprised entirely of punctuation characters, which punctuation characters have been carefully chosen so as to look like what they mean. E.g., asterisks around a word actually look like \*emphasis\*. Markdown lists look like, well, lists. Even blockquotes look like quoted passages of text, assuming you've ever used email.

    Inline HTML

    Markdown's syntax is intended for one purpose: to be used as a format for *writing* for the web. Markdown is not a replacement for HTML, or even close to it. Its syntax is very small, corresponding only to a very small subset of HTML tags. The idea is *not* to create a syntax that makes it easier to insert HTML tags. In my opinion, HTML tags are already easy to insert. The idea for Markdown is to make it easy to read, write, and edit prose. HTML is a *publishing* format; Markdown is a *writing* format. Thus, Markdown's formatting syntax only addresses issues that can be conveyed in plain text. For any markup that is not covered by Markdown's syntax, you simply use HTML itself. There's no need to preface it or delimit it to indicate that you're switching from Markdown to HTML; you just use the tags. The only restrictions are that block-level HTML elements -- e.g. `
    `, ``, `
    `, `

    `, etc. -- must be separated from surrounding content by blank lines, and the start and end tags of the block should not be indented with tabs or spaces. Markdown is smart enough not to add extra (unwanted) `

    ` tags around HTML block-level tags. For example, to add an HTML table to a Markdown article: This is a regular paragraph.

    Foo
    This is another regular paragraph. Note that Markdown formatting syntax is not processed within block-level HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an HTML block. Span-level HTML tags -- e.g. ``, ``, or `` -- can be used anywhere in a Markdown paragraph, list item, or header. If you want, you can even use HTML tags instead of Markdown formatting; e.g. if you'd prefer to use HTML `` or `` tags instead of Markdown's link or image syntax, go right ahead. Unlike block-level HTML tags, Markdown syntax *is* processed within span-level tags.

    Automatic Escaping for Special Characters

    In HTML, there are two characters that demand special treatment: `<` and `&`. Left angle brackets are used to start tags; ampersands are used to denote HTML entities. If you want to use them as literal characters, you must escape them as entities, e.g. `<`, and `&`. Ampersands in particular are bedeviling for web writers. If you want to write about 'AT&T', you need to write '`AT&T`'. You even need to escape ampersands within URLs. Thus, if you want to link to: http://images.google.com/images?num=30&q=larry+bird you need to encode the URL as: http://images.google.com/images?num=30&q=larry+bird in your anchor tag `href` attribute. Needless to say, this is easy to forget, and is probably the single most common source of HTML validation errors in otherwise well-marked-up web sites. Markdown allows you to use these characters naturally, taking care of all the necessary escaping for you. If you use an ampersand as part of an HTML entity, it remains unchanged; otherwise it will be translated into `&`. So, if you want to include a copyright symbol in your article, you can write: © and Markdown will leave it alone. But if you write: AT&T Markdown will translate it to: AT&T Similarly, because Markdown supports [inline HTML](#html), if you use angle brackets as delimiters for HTML tags, Markdown will treat them as such. But if you write: 4 < 5 Markdown will translate it to: 4 < 5 However, inside Markdown code spans and blocks, angle brackets and ampersands are *always* encoded automatically. This makes it easy to use Markdown to write about HTML code. (As opposed to raw HTML, which is a terrible format for writing about HTML syntax, because every single `<` and `&` in your example code needs to be escaped.) * * *

    Block Elements

    Paragraphs and Line Breaks

    A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line -- a line containing nothing but spaces or tabs is considered blank.) Normal paragraphs should not be intended with spaces or tabs. The implication of the "one or more consecutive lines of text" rule is that Markdown supports "hard-wrapped" text paragraphs. This differs significantly from most other text-to-HTML formatters (including Movable Type's "Convert Line Breaks" option) which translate every line break character in a paragraph into a `
    ` tag. When you *do* want to insert a `
    ` break tag using Markdown, you end a line with two or more spaces, then type return. Yes, this takes a tad more effort to create a `
    `, but a simplistic "every line break is a `
    `" rule wouldn't work for Markdown. Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l] work best -- and look better -- when you format them with hard breaks. [bq]: #blockquote [l]: #list Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. Setext-style headers are "underlined" using equal signs (for first-level headers) and dashes (for second-level headers). For example: This is an H1 ============= This is an H2 ------------- Any number of underlining `=`'s or `-`'s will work. Atx-style headers use 1-6 hash characters at the start of the line, corresponding to header levels 1-6. For example: # This is an H1 ## This is an H2 ###### This is an H6 Optionally, you may "close" atx-style headers. This is purely cosmetic -- you can use this if you think it looks better. The closing hashes don't even need to match the number of hashes used to open the header. (The number of opening hashes determines the header level.) : # This is an H1 # ## This is an H2 ## ### This is an H3 ######

    Blockquotes

    Markdown uses email-style `>` characters for blockquoting. If you're familiar with quoting passages of text in an email message, then you know how to create a blockquote in Markdown. It looks best if you hard wrap the text and put a `>` before every line: > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. > > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse > id sem consectetuer libero luctus adipiscing. Markdown allows you to be lazy and only put the `>` before the first line of a hard-wrapped paragraph: > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by adding additional levels of `>`: > This is the first level of quoting. > > > This is nested blockquote. > > Back to the first level. Blockquotes can contain other Markdown elements, including headers, lists, and code blocks: > ## This is a header. > > 1. This is the first list item. > 2. This is the second list item. > > Here's some example code: > > return shell_exec("echo $input | $markdown_script"); Any decent text editor should make email-style quoting easy. For example, with BBEdit, you can make a selection and choose Increase Quote Level from the Text menu.

    Lists

    Markdown supports ordered (numbered) and unordered (bulleted) lists. Unordered lists use asterisks, pluses, and hyphens -- interchangably -- as list markers: * Red * Green * Blue is equivalent to: + Red + Green + Blue and: - Red - Green - Blue Ordered lists use numbers followed by periods: 1. Bird 2. McHale 3. Parish It's important to note that the actual numbers you use to mark the list have no effect on the HTML output Markdown produces. The HTML Markdown produces from the above list is:
    1. Bird
    2. McHale
    3. Parish
    If you instead wrote the list in Markdown like this: 1. Bird 1. McHale 1. Parish or even: 3. Bird 1. McHale 8. Parish you'd get the exact same HTML output. The point is, if you want to, you can use ordinal numbers in your ordered Markdown lists, so that the numbers in your source match the numbers in your published HTML. But if you want to be lazy, you don't have to. If you do use lazy list numbering, however, you should still start the list with the number 1. At some point in the future, Markdown may support starting ordered lists at an arbitrary number. List markers typically start at the left margin, but may be indented by up to three spaces. List markers must be followed by one or more spaces or a tab. To make lists look nice, you can wrap items with hanging indents: * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. But if you want to be lazy, you don't have to: * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse id sem consectetuer libero luctus adipiscing. If list items are separated by blank lines, Markdown will wrap the items in `

    ` tags in the HTML output. For example, this input: * Bird * Magic will turn into:

    • Bird
    • Magic
    But this: * Bird * Magic will turn into:
    • Bird

    • Magic

    List items may consist of multiple paragraphs. Each subsequent paragraph in a list item must be intended by either 4 spaces or one tab: 1. This is a list item with two paragraphs. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. Donec sit amet nisl. Aliquam semper ipsum sit amet velit. 2. Suspendisse id sem consectetuer libero luctus adipiscing. It looks nice if you indent every line of the subsequent paragraphs, but here again, Markdown will allow you to be lazy: * This is a list item with two paragraphs. This is the second paragraph in the list item. You're only required to indent the first line. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. * Another item in the same list. To put a blockquote within a list item, the blockquote's `>` delimiters need to be indented: * A list item with a blockquote: > This is a blockquote > inside a list item. To put a code block within a list item, the code block needs to be indented *twice* -- 8 spaces or two tabs: * A list item with a code block: It's worth noting that it's possible to trigger an ordered list by accident, by writing something like this: 1986. What a great season. In other words, a *number-period-space* sequence at the beginning of a line. To avoid this, you can backslash-escape the period: 1986\. What a great season.

    Code Blocks

    Pre-formatted code blocks are used for writing about programming or markup source code. Rather than forming normal paragraphs, the lines of a code block are interpreted literally. Markdown wraps a code block in both `
    ` and `` tags.
    
    To produce a code block in Markdown, simply indent every line of the
    block by at least 4 spaces or 1 tab. For example, given this input:
    
        This is a normal paragraph:
    
            This is a code block.
    
    Markdown will generate:
    
        

    This is a normal paragraph:

    This is a code block.
        
    One level of indentation -- 4 spaces or 1 tab -- is removed from each line of the code block. For example, this: Here is an example of AppleScript: tell application "Foo" beep end tell will turn into:

    Here is an example of AppleScript:

    tell application "Foo"
            beep
        end tell
        
    A code block continues until it reaches a line that is not indented (or the end of the article). Within a code block, ampersands (`&`) and angle brackets (`<` and `>`) are automatically converted into HTML entities. This makes it very easy to include example HTML source code using Markdown -- just paste it and indent it, and Markdown will handle the hassle of encoding the ampersands and angle brackets. For example, this: will turn into:
    <div class="footer">
            &copy; 2004 Foo Corporation
        </div>
        
    Regular Markdown syntax is not processed within code blocks. E.g., asterisks are just literal asterisks within a code block. This means it's also easy to use Markdown to write about Markdown's own syntax.

    Horizontal Rules

    You can produce a horizontal rule tag (`
    `) by placing three or more hyphens, asterisks, or underscores on a line by themselves. If you wish, you may use spaces between the hyphens or asterisks. Each of the following lines will produce a horizontal rule: * * * *** ***** - - - --------------------------------------- _ _ _ * * *

    Span Elements

    Markdown supports two style of links: *inline* and *reference*. In both styles, the link text is delimited by [square brackets]. To create an inline link, use a set of regular parentheses immediately after the link text's closing square bracket. Inside the parentheses, put the URL where you want the link to point, along with an *optional* title for the link, surrounded in quotes. For example: This is [an example](http://example.com/ "Title") inline link. [This link](http://example.net/) has no title attribute. Will produce:

    This is an example inline link.

    This link has no title attribute.

    If you're referring to a local resource on the same server, you can use relative paths: See my [About](/about/) page for details. Reference-style links use a second set of square brackets, inside which you place a label of your choosing to identify the link: This is [an example][id] reference-style link. You can optionally use a space to separate the sets of brackets: This is [an example] [id] reference-style link. Then, anywhere in the document, you define your link label like this, on a line by itself: [id]: http://example.com/ "Optional Title Here" That is: * Square brackets containing the link identifier (optionally indented from the left margin using up to three spaces); * followed by a colon; * followed by one or more spaces (or tabs); * followed by the URL for the link; * optionally followed by a title attribute for the link, enclosed in double or single quotes. The link URL may, optionally, be surrounded by angle brackets: [id]: "Optional Title Here" You can put the title attribute on the next line and use extra spaces or tabs for padding, which tends to look better with longer URLs: [id]: http://example.com/longish/path/to/resource/here "Optional Title Here" Link definitions are only used for creating links during Markdown processing, and are stripped from your document in the HTML output. Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are *not* case sensitive. E.g. these two links: [link text][a] [link text][A] are equivalent. The *implicit link name* shortcut allows you to omit the name of the link, in which case the link text itself is used as the name. Just use an empty set of square brackets -- e.g., to link the word "Google" to the google.com web site, you could simply write: [Google][] And then define the link: [Google]: http://google.com/ Because link names may contain spaces, this shortcut even works for multiple words in the link text: Visit [Daring Fireball][] for more information. And then define the link: [Daring Fireball]: http://daringfireball.net/ Link definitions can be placed anywhere in your Markdown document. I tend to put them immediately after each paragraph in which they're used, but if you want, you can put them all at the end of your document, sort of like footnotes. Here's an example of reference links in action: I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]. [1]: http://google.com/ "Google" [2]: http://search.yahoo.com/ "Yahoo Search" [3]: http://search.msn.com/ "MSN Search" Using the implicit link name shortcut, you could instead write: I get 10 times more traffic from [Google][] than from [Yahoo][] or [MSN][]. [google]: http://google.com/ "Google" [yahoo]: http://search.yahoo.com/ "Yahoo Search" [msn]: http://search.msn.com/ "MSN Search" Both of the above examples will produce the following HTML output:

    I get 10 times more traffic from Google than from Yahoo or MSN.

    For comparison, here is the same paragraph written using Markdown's inline link style: I get 10 times more traffic from [Google](http://google.com/ "Google") than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or [MSN](http://search.msn.com/ "MSN Search"). The point of reference-style links is not that they're easier to write. The point is that with reference-style links, your document source is vastly more readable. Compare the above examples: using reference-style links, the paragraph itself is only 81 characters long; with inline-style links, it's 176 characters; and as raw HTML, it's 234 characters. In the raw HTML, there's more markup than there is text. With Markdown's reference-style links, a source document much more closely resembles the final output, as rendered in a browser. By allowing you to move the markup-related metadata out of the paragraph, you can add links without interrupting the narrative flow of your prose.

    Emphasis

    Markdown treats asterisks (`*`) and underscores (`_`) as indicators of emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML `` tag. E.g., this input: *single asterisks* _single underscores_ **double asterisks** __double underscores__ will produce: single asterisks single underscores double asterisks double underscores You can use whichever style you prefer; the lone restriction is that the same character must be used to open and close an emphasis span. Emphasis can be used in the middle of a word: un*fucking*believable But if you surround an `*` or `_` with spaces, it'll be treated as a literal asterisk or underscore. To produce a literal asterisk or underscore at a position where it would otherwise be used as an emphasis delimiter, you can backslash escape it: \*this text is surrounded by literal asterisks\*

    Code

    To indicate a span of code, wrap it with backtick quotes (`` ` ``). Unlike a pre-formatted code block, a code span indicates code within a normal paragraph. For example: Use the `printf()` function. will produce:

    Use the printf() function.

    To include a literal backtick character within a code span, you can use multiple backticks as the opening and closing delimiters: ``There is a literal backtick (`) here.`` which will produce this:

    There is a literal backtick (`) here.

    The backtick delimiters surrounding a code span may include spaces -- one after the opening, one before the closing. This allows you to place literal backtick characters at the beginning or end of a code span: A single backtick in a code span: `` ` `` A backtick-delimited string in a code span: `` `foo` `` will produce:

    A single backtick in a code span: `

    A backtick-delimited string in a code span: `foo`

    With a code span, ampersands and angle brackets are encoded as HTML entities automatically, which makes it easy to include example HTML tags. Markdown will turn this: Please don't use any `` tags. into:

    Please don't use any <blink> tags.

    You can write this: `—` is the decimal-encoded equivalent of `—`. to produce:

    &#8212; is the decimal-encoded equivalent of &mdash;.

    Images

    Admittedly, it's fairly difficult to devise a "natural" syntax for placing images into a plain text document format. Markdown uses an image syntax that is intended to resemble the syntax for links, allowing for two styles: *inline* and *reference*. Inline image syntax looks like this: ![Alt text](/path/to/img.jpg) ![Alt text](/path/to/img.jpg "Optional title") That is: * An exclamation mark: `!`; * followed by a set of square brackets, containing the `alt` attribute text for the image; * followed by a set of parentheses, containing the URL or path to the image, and an optional `title` attribute enclosed in double or single quotes. Reference-style image syntax looks like this: ![Alt text][id] Where "id" is the name of a defined image reference. Image references are defined using syntax identical to link references: [id]: url/to/image "Optional title attribute" As of this writing, Markdown has no syntax for specifying the dimensions of an image; if this is important to you, you can simply use regular HTML `` tags. * * *

    Miscellaneous

    Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this: Markdown will turn this into: http://example.com/ Automatic links for email addresses work similarly, except that Markdown will also perform a bit of randomized decimal and hex entity-encoding to help obscure your address from address-harvesting spambots. For example, Markdown will turn this: into something like this: address@exa mple.com which will render in a browser as a clickable link to "address@example.com". (This sort of entity-encoding trick will indeed fool many, if not most, address-harvesting bots, but it definitely won't fool all of them. It's better than nothing, but an address published in this way will probably eventually start receiving spam.)

    Backslash Escapes

    Markdown allows you to use backslash escapes to generate literal characters which would otherwise have special meaning in Markdown's formatting syntax. For example, if you wanted to surround a word with literal asterisks (instead of an HTML `` tag), you can backslashes before the asterisks, like this: \*literal asterisks\* Markdown provides backslash escapes for the following characters: \ backslash ` backtick * asterisk _ underscore {} curly braces [] square brackets () parentheses # hash mark + plus sign - minus sign (hyphen) . dot ! exclamation mark cl-markdown-20101006-darcs/unit-tests/markdown-tests/Markdown Documentation - Basics.text0000644000175000017500000001760011453110206027567 0ustar xachxachMarkdown: Basics ================ Getting the Gist of Markdown's Formatting Syntax ------------------------------------------------ This page offers a brief overview of what it's like to use Markdown. The [syntax page] [s] provides complete, detailed documentation for every feature, but Markdown should be very easy to pick up simply by looking at a few examples of it in action. The examples on this page are written in a before/after style, showing example syntax and the HTML output produced by Markdown. It's also helpful to simply try Markdown out; the [Dingus] [d] is a web application that allows you type your own Markdown-formatted text and translate it to XHTML. **Note:** This document is itself written using Markdown; you can [see the source for it by adding '.text' to the URL] [src]. [s]: /projects/markdown/syntax "Markdown Syntax" [d]: /projects/markdown/dingus "Markdown Dingus" [src]: /projects/markdown/basics.text ## Paragraphs, Headers, Blockquotes ## A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. (A blank line is any line that looks like a blank line -- a line containing nothing spaces or tabs is considered blank.) Normal paragraphs should not be intended with spaces or tabs. Markdown offers two styles of headers: *Setext* and *atx*. Setext-style headers for `

    ` and `

    ` are created by "underlining" with equal signs (`=`) and hyphens (`-`), respectively. To create an atx-style header, you put 1-6 hash marks (`#`) at the beginning of the line -- the number of hashes equals the resulting HTML header level. Blockquotes are indicated using email-style '`>`' angle brackets. Markdown: A First Level Header ==================== A Second Level Header --------------------- Now is the time for all good men to come to the aid of their country. This is just a regular paragraph. The quick brown fox jumped over the lazy dog's back. ### Header 3 > This is a blockquote. > > This is the second paragraph in the blockquote. > > ## This is an H2 in a blockquote Output:

    A First Level Header

    A Second Level Header

    Now is the time for all good men to come to the aid of their country. This is just a regular paragraph.

    The quick brown fox jumped over the lazy dog's back.

    Header 3

    This is a blockquote.

    This is the second paragraph in the blockquote.

    This is an H2 in a blockquote

    ### Phrase Emphasis ### Markdown uses asterisks and underscores to indicate spans of emphasis. Markdown: Some of these words *are emphasized*. Some of these words _are emphasized also_. Use two asterisks for **strong emphasis**. Or, if you prefer, __use two underscores instead__. Output:

    Some of these words are emphasized. Some of these words are emphasized also.

    Use two asterisks for strong emphasis. Or, if you prefer, use two underscores instead.

    ## Lists ## Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`, `+`, and `-`) as list markers. These three markers are interchangable; this: * Candy. * Gum. * Booze. this: + Candy. + Gum. + Booze. and this: - Candy. - Gum. - Booze. all produce the same output:
    • Candy.
    • Gum.
    • Booze.
    Ordered (numbered) lists use regular numbers, followed by periods, as list markers: 1. Red 2. Green 3. Blue Output:
    1. Red
    2. Green
    3. Blue
    If you put blank lines between items, you'll get `

    ` tags for the list item text. You can create multi-paragraph list items by indenting the paragraphs by 4 spaces or 1 tab: * A list item. With multiple paragraphs. * Another item in the list. Output:

    • A list item.

      With multiple paragraphs.

    • Another item in the list.

    ### Links ### Markdown supports two styles for creating links: *inline* and *reference*. With both styles, you use square brackets to delimit the text you want to turn into a link. Inline-style links use parentheses immediately after the link text. For example: This is an [example link](http://example.com/). Output:

    This is an example link.

    Optionally, you may include a title attribute in the parentheses: This is an [example link](http://example.com/ "With a Title"). Output:

    This is an example link.

    Reference-style links allow you to refer to your links by names, which you define elsewhere in your document: I get 10 times more traffic from [Google][1] than from [Yahoo][2] or [MSN][3]. [1]: http://google.com/ "Google" [2]: http://search.yahoo.com/ "Yahoo Search" [3]: http://search.msn.com/ "MSN Search" Output:

    I get 10 times more traffic from Google than from Yahoo or MSN.

    The title attribute is optional. Link names may contain letters, numbers and spaces, but are *not* case sensitive: I start my morning with a cup of coffee and [The New York Times][NY Times]. [ny times]: http://www.nytimes.com/ Output:

    I start my morning with a cup of coffee and The New York Times.

    ### Images ### Image syntax is very much like link syntax. Inline (titles are optional): ![alt text](/path/to/img.jpg "Title") Reference-style: ![alt text][id] [id]: /path/to/img.jpg "Title" Both of the above examples produce the same output: alt text ### Code ### In a regular paragraph, you can create code span by wrapping text in backtick quotes. Any ampersands (`&`) and angle brackets (`<` or `>`) will automatically be translated into HTML entities. This makes it easy to use Markdown to write about HTML example code: I strongly recommend against using any `` tags. I wish SmartyPants used named entities like `—` instead of decimal-encoded entites like `—`. Output:

    I strongly recommend against using any <blink> tags.

    I wish SmartyPants used named entities like &mdash; instead of decimal-encoded entites like &#8212;.

    To specify an entire block of pre-formatted code, indent every line of the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`, and `>` characters will be escaped automatically. Markdown: If you want your page to validate under XHTML 1.0 Strict, you've got to put paragraph tags in your blockquotes:

    For example.

    Output:

    If you want your page to validate under XHTML 1.0 Strict, you've got to put paragraph tags in your blockquotes:

    <blockquote>
            <p>For example.</p>
        </blockquote>
        
    cl-markdown-20101006-darcs/unit-tests/markdown-tests/Inline HTML (Simple).text0000644000175000017500000000105111453110206025200 0ustar xachxachHere's a simple block:
    foo
    This should be a code block, though:
    foo
    As should this:
    foo
    Now, nested:
    foo
    This should just be an HTML comment: Multiline: Code block: Just plain comment, with trailing spaces on the line: Code:
    Hr's:








    cl-markdown-20101006-darcs/unit-tests/markdown-tests/Strong and em together.text0000644000175000017500000000015311453110206026067 0ustar xachxach***This is strong and em.*** So is ***this*** word. ___This is strong and em.___ So is ___this___ word. cl-markdown-20101006-darcs/unit-tests/markdown-tests/Inline HTML comments.text0000644000175000017500000000024411453110206025456 0ustar xachxachParagraph one. Paragraph two. The end. ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootcl-markdown-20101006-darcs/unit-tests/markdown-tests/Hard-wrapped paragraphs with list-like lines.textcl-markdown-20101006-darcs/unit-tests/markdown-tests/Hard-wrapped paragraphs with list-like lines.te0000644000175000017500000000030511453110206031663 0ustar xachxachIn Markdown 1.0.0 and earlier. Version 8. This line turns into a list item. Because a hard-wrapped line in the middle of a paragraph looked like a list item. Here's one with a bullet. * criminey. cl-markdown-20101006-darcs/unit-tests/markdown-tests/Blockquotes with code blocks.text0000644000175000017500000000010711453110206027263 0ustar xachxach> Example: > > sub status { > print "working"; > } > cl-markdown-20101006-darcs/unit-tests/markdown-tests/Horizontal rules.text0000644000175000017500000000041611453110206025152 0ustar xachxachDashes: --- --- --- --- --- - - - - - - - - - - - - - - - Asterisks: *** *** *** *** *** * * * * * * * * * * * * * * * Underscores: ___ ___ ___ ___ ___ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ cl-markdown-20101006-darcs/unit-tests/markdown-tests/Amps and angle encoding.text0000644000175000017500000000057511453110206026155 0ustar xachxachAT&T has an ampersand in their name. AT&T is another way to write it. This & that. 4 < 5. 6 > 5. Here's a [link] [1] with an ampersand in the URL. Here's a link with an amersand in the link text: [AT&T] [2]. Here's an inline [link](/script?foo=1&bar=2). Here's an inline [link](). [1]: http://example.com/?foo=1&bar=2 [2]: http://att.com/ "AT&T"cl-markdown-20101006-darcs/unit-tests/markdown-tests/Inline HTML (Advanced).text0000644000175000017500000000020611453110206025455 0ustar xachxachSimple block on one line:
    foo
    And nested without indentation:
    foo
    bar
    cl-markdown-20101006-darcs/unit-tests/markdown-tests/Ordered and unordered lists.text0000644000175000017500000000151111453110206027101 0ustar xachxach## Unordered Asterisks tight: * asterisk 1 * asterisk 2 * asterisk 3 Asterisks loose: * asterisk 1 * asterisk 2 * asterisk 3 * * * Pluses tight: + Plus 1 + Plus 2 + Plus 3 Pluses loose: + Plus 1 + Plus 2 + Plus 3 * * * Minuses tight: - Minus 1 - Minus 2 - Minus 3 Minuses loose: - Minus 1 - Minus 2 - Minus 3 ## Ordered Tight: 1. First 2. Second 3. Third and: 1. One 2. Two 3. Three Loose using tabs: 1. First 2. Second 3. Third and using spaces: 1. One 2. Two 3. Three Multiple paragraphs: 1. Item 1, graf one. Item 2. graf two. The quick brown fox jumped over the lazy dog's back. 2. Item 2. 3. Item 3. ## Nested * Tab * Tab * Tab Here's another: 1. First 2. Second: * Fee * Fie * Foe 3. Third Same thing but with paragraphs: 1. First 2. Second: * Fee * Fie * Foe 3. Third cl-markdown-20101006-darcs/unit-tests/markdown-tests/Backslash escapes.text0000644000175000017500000000157111453110206025210 0ustar xachxachThese should all get escaped: Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: \( Right paren: \) Greater-than: \> Hash: \# Period: \. Bang: \! Plus: \+ Minus: \- These should not, because they occur within a code block: Backslash: \\ Backtick: \` Asterisk: \* Underscore: \_ Left brace: \{ Right brace: \} Left bracket: \[ Right bracket: \] Left paren: \( Right paren: \) Greater-than: \> Hash: \# Period: \. Bang: \! Plus: \+ Minus: \- Nor should these, which occur in code spans: Backslash: `\\` Backtick: `` \` `` Asterisk: `\*` Underscore: `\_` Left brace: `\{` Right brace: `\}` Left bracket: `\[` Right bracket: `\]` Left paren: `\(` Right paren: `\)` Greater-than: `\>` Hash: `\#` Period: `\.` Bang: `\!` Plus: `\+` Minus: `\-` cl-markdown-20101006-darcs/unit-tests/markdown-tests/Nested blockquotes.text0000644000175000017500000000003011453110206025434 0ustar xachxach> foo > > > bar > > foo cl-markdown-20101006-darcs/unit-tests/markdown-tests/Links, reference style.text0000644000175000017500000000042111453110206026076 0ustar xachxachFoo [bar] [1]. Foo [bar][1]. Foo [bar] [1]. [1]: /url/ "Title" With [embedded [brackets]] [b]. Indented [once][]. Indented [twice][]. Indented [thrice][]. Indented [four][] times. [once]: /url [twice]: /url [thrice]: /url [four]: /url [b]: /url/ cl-markdown-20101006-darcs/unit-tests/test-strippers.lisp0000644000175000017500000000116211453110206021716 0ustar xachxach(in-package #:cl-markdown-test) (deftestsuite test-strippers () ()) (deftestsuite test-one-tab-stripper (test-strippers) () (:equality-test 'samep)) (addtest test-spaces-1 (ensure-same (one-tab-stripper " hello") (values "hello" t))) (addtest test-tabs-1 (ensure-same (one-tab-stripper (concatenate 'string (string #\Tab) "hello")) (values "hello" t))) (addtest test-tabs-2 (ensure-same (one-tab-stripper (concatenate 'string (string #\Tab) " hello")) (values " hello" t))) (addtest test-spaces-2 (ensure-same (one-tab-stripper " hello") (values " hello" nil)))cl-markdown-20101006-darcs/unit-tests/utilities.lisp0000644000175000017500000000320211453110206020716 0ustar xachxach(in-package #:cl-markdown-test) (defun strip-html (string) (with-output-to-string (out) (flet ((emit (ch) (write-char ch out))) (let ((quote? nil) (bracket? nil) (index 0)) (loop while (< index (length string)) do (let ((ch (aref string index))) (cond ((char= ch #\\) (emit ch) (emit (aref string (incf index)))) ((char= ch #\") (setf quote? (not quote?)) (emit ch)) ((and (not quote?) (char= ch #\<)) (setf bracket? t)) ((and bracket? (char= ch #\>)) (setf bracket? nil)) (bracket? ;; skip it ) (t (emit ch))) (incf index))))))) (defun compare-line-by-line (a b &key (key 'identity) (test 'string=)) (setf key (coerce key 'function)) (setf test (coerce test 'function)) (let ((ia (make-iterator a :treat-contents-as :lines)) (ib (make-iterator b :treat-contents-as :lines))) (map-containers (lambda (la lb) (unless (funcall test (funcall key la) (funcall key lb)) (return-from compare-line-by-line nil))) ia ib) (and (null (move-forward-p ia)) (null (move-forward-p ib))))) #+(or) (defun compare-line-by-line (a b &key (key 'identity) (test 'string=)) (setf key (coerce key 'function)) (setf test (coerce test 'function)) (with-input-from-string (sa a) (with-input-from-string (sb b) (loop for la = (read-line sa nil nil) for lb = (read-line sb nil nil) when (and (not la) (not lb)) do (return t) when (or (not la) (not lb) (not (funcall test (funcall key la) (funcall key lb)))) do (return nil) finally (return t))))) #+(or) (compare-line-by-line "a b c" "a b c" ) cl-markdown-20101006-darcs/unit-tests/test-headers.lisp0000644000175000017500000000142511453110206021300 0ustar xachxach(in-package #:cl-markdown-test) #+(or) (run-tests :suite 'test-headers) (deftestsuite test-headers (test-snippets) () (:documentation "Case 272")) (addtest (test-headers) one-dash (check-output "asdf -")) (addtest (test-headers) two-dash (check-output "asdf --")) (addtest (test-headers) three-dash (check-output "asdf ---")) (addtest (test-headers) three-dash-with-whitespace (check-output "asdf --- ")) (addtest (test-headers) four-dash (check-output "asdf ----")) (addtest (test-headers) five-dash (check-output "asdf -----")) (addtest (test-headers) six-dash (check-output "asdf ------")) (addtest (test-headers) starts-with-dashes (check-output "asdf -- it's the bomb")) (addtest (test-headers) really-an-hr (check-output " ---")) cl-markdown-20101006-darcs/unit-tests/test-brackets-and-includes.lisp0000644000175000017500000000532311453110206024030 0ustar xachxach(in-package #:cl-markdown-test) (deftestsuite brackets-and-includes (cl-markdown-test) ((temporary-directory "/tmp/")) (:setup (with-new-file (out (relative-pathname temporary-directory "bandi-1.md")) (format out " {set-property slush \"1234-simple\"} This is true.\{footnote \"technically, this is true\"}. Did you: * like it? * love it? * find it irrelevant? ")) (with-new-file (out (relative-pathname temporary-directory "bandi-2.md")) (format out " {set-property slush \"1234-complex\"} This is true.\{footnote \"actually it's not only false but also 1. misleading 2. incorrect 3. overly optimistic. Let you conscience by your guide.\"}")))) (addtest (brackets-and-includes) include-simple (let ((output (nth-value 1 (markdown (concatenate 'string "Including bandi-1.md {include " (namestring (relative-pathname temporary-directory "bandi-1.md")) "} slush: {property slush} Lets show the footnotes: {footnotes} All done.") :stream nil)))) (ensure (search "like it?" output :test 'char=) :report "footnote not found") (ensure (search "1234-simple" output :test 'char=) :report "property not found"))) (addtest (brackets-and-includes) include-complex (let ((output (nth-value 1 (markdown (concatenate 'string "Including bandi-2.md {include " (namestring (relative-pathname temporary-directory "bandi-2.md")) "} slush: {property slush} Lets show the footnotes: {footnotes} All done.") :stream nil)))) (ensure (search "misleading" output :test 'char=) :report "footnote not found") (ensure (search "1234-complex" output :test 'char=) :report "property not found"))) (deftestsuite include-if (cl-markdown-test) ((temporary-directory "/tmp/")) (:setup (with-new-file (out (relative-pathname temporary-directory "bandi-1.md")) (format out " {set-property slush \"1234-simple\"} This is true.\{footnote \"technically, this is true\"}. Did you: * like it? * love it? * find it irrelevant? ")))) (addtest (include-if) property-not-set (let ((text (nth-value 1 (markdown (concatenate 'string "# Title {include-if test-prop " (namestring (relative-pathname temporary-directory "bandi-1.md")) "} paragraph") :stream nil :properties `((test-prop . nil)))))) (ensure-null (search "This is true" text :test 'char=)))) (addtest (include-if) property-set (let ((text (nth-value 1 (markdown (concatenate 'string "# Title {include-if test-prop " (namestring (relative-pathname temporary-directory "bandi-1.md")) "} paragraph") :stream nil :properties `((test-prop . t)))))) (ensure (search "This is true" text :test 'char=))))cl-markdown-20101006-darcs/unit-tests/test-links.lisp0000644000175000017500000000635111453110206021010 0ustar xachxach(in-package #:cl-markdown-test) (deftestsuite test-reference-links (test-snippets) ()) (addtest (test-reference-links) title-only-1 (let ((doc (cl-markdown:markdown "I like [beans][]. Do you? [beans]: http://www.beans.com \"foo\" " :stream :none))) (ensure-same (properties (first-element (link-info doc))) nil) (ensure-same (title (first-element (link-info doc))) "foo" :test 'string=))) (addtest (test-reference-links) title-only-2-a (let ((doc (cl-markdown:markdown "I like [beans][]. Do you? [beans]: http://www.beans.com \(foo is a bean\) " :stream :none))) (ensure-same (properties (first-element (link-info doc))) nil) (ensure-same (title (first-element (link-info doc))) "foo is a bean" :test 'string=))) (addtest (test-reference-links) title-only-2-b (let ((doc (cl-markdown:markdown "I like [beans][]. Do you? [beans]: http://www.beans.com \"foo is a bean\" " :stream :none))) (ensure-same (properties (first-element (link-info doc))) nil) (ensure-same (title (first-element (link-info doc))) "foo is a bean" :test 'string=))) (addtest (test-reference-links) properties-only-1 (let ((doc (cl-markdown:markdown "I like [beans][]. Do you? [beans]: http://www.beans.com target new class \"external link\" " :stream :none))) (ensure-same (properties (first-element (link-info doc))) '((:target . "new") (:class . "external link")) :test 'equalp) (ensure-null (title (first-element (link-info doc)))))) (addtest (test-reference-links) title-and-properties-1 (let ((doc (cl-markdown:markdown "I like [beans][]. Do you? [beans]: http://www.beans.com \"beans are the new black\" target new " :stream :none))) (ensure-same (properties (first-element (link-info doc))) '((:target . "new")) :test 'equalp) (ensure-same (title (first-element (link-info doc))) "beans are the new black" :test 'string=))) (addtest (test-reference-links) title-and-properties-2 (let ((doc (cl-markdown:markdown "I like [beans][]. Do you? [beans]: http://www.beans.com \"beans are the new black\" target new class external " :stream :none))) (ensure-same (properties (first-element (link-info doc))) '((:target . "new") (:class . "external")) :test 'equalp) (ensure-same (title (first-element (link-info doc))) "beans are the new black" :test 'string=))) ;; not sure how this should work (addtest (test-reference-links :expected-failure "parsing multi-line reference links") title-and-properties-3 (let ((doc (cl-markdown:markdown "I like [beans][]. Do you? [beans]: http://www.beans.com \"beans are the new black\" target new class external " :stream :none))) (ensure-same (properties (first-element (link-info doc))) '((:target . "new") (:class . "external")) :test 'equalp) (ensure-same (title (first-element (link-info doc))) "beans are the new black" :test 'string=))) (addtest (test-reference-links :expected-failure "parsing multi-line reference links") title-on-new-line (let ((doc (cl-markdown:markdown "I like [beans][]. Do you? [beans]: http://www.beans.com \"beans are the new black\" " :stream :none))) (ensure-same (title (first-element (link-info doc))) "beans are the new black" :test 'string=))) cl-markdown-20101006-darcs/unit-tests/test-spans.lisp0000644000175000017500000000660311453110206021014 0ustar xachxach(in-package #:cl-markdown-test) (deftestsuite test-spans (cl-markdown-test-all) ()) (addtest (test-spans) test-1-replacement (ensure-same (scan-lines-with-scanners (list "Can you say **strong**?") `((,(create-scanner '(:sequence strong-1)) strong) (,(create-scanner '(:sequence strong-2)) strong))) '("Can you say " (STRONG "strong") "?") :test 'equalp)) (addtest (test-spans) test-2-replacement (ensure-same (scan-lines-with-scanners (list "Can you say **strong**? I can **say** strong!") `((,(create-scanner '(:sequence strong-1)) strong) (,(create-scanner '(:sequence strong-2)) strong))) '("Can you say " (STRONG "strong") "? I can " (STRONG "say") " strong!") :test 'equalp)) ;;; --------------------------------------------------------------------------- (addtest (test-spans) test-inline-link (ensure-same (scan-lines-with-scanners (list "This is [Google](http://google.com/). OK") `((,(create-scanner '(:sequence inline-link)) inline-link))) '("This is " (INLINE-LINK "Google" "http://google.com/" NIL) ". OK") :test 'equalp)) ;;; --------------------------------------------------------------------------- (addtest (test-spans) test-inline-link-with-title (ensure-same (scan-lines-with-scanners (list "This is [Google](http://google.com/ \"A nice title\"). OK") `((,(create-scanner '(:sequence inline-link)) inline-link))) '("This is " (INLINE-LINK "Google" "http://google.com/" "A nice title") ". OK") :test 'equalp)) ;;; --------------------------------------------------------------------------- (addtest (test-spans) test-reference-link-1 (ensure-same (scan-lines-with-scanners (list "This is [Google][Foo]. OK") `((,(create-scanner '(:sequence reference-link)) reference-link))) '("This is " (reference-link "Google" "Foo") ". OK") :test 'equalp)) (addtest (test-spans) test-reference-link-2 (ensure-same (scan-lines-with-scanners (list "This is [Google] [Foo]. OK") `((,(create-scanner '(:sequence reference-link)) reference-link))) '("This is " (reference-link "Google" "Foo") ". OK") :test 'equalp)) (addtest (test-spans) test-reference-link-implicit (ensure-same (scan-lines-with-scanners (list "This is [Google][]. OK") `((,(create-scanner '(:sequence reference-link)) reference-link))) '("This is " (reference-link "Google" "") ". OK") :test 'equalp)) (addtest (test-spans) test-reference-link-with-spaces (ensure-same (scan-lines-with-scanners (list "This is [Daring Fireball][]. OK") `((,(create-scanner '(:sequence reference-link)) reference-link))) '("This is " (reference-link "Daring Fireball" "") ". OK") :test 'equalp)) (deftestsuite test-strong-2 (test-spans) ((scanner (create-scanner '(:sequence (:greedy-repetition 2 2 #\*) (:register (:sequence (:greedy-repetition 0 nil (:inverted-char-class #\*)))) (:greedy-repetition 2 2 #\*)))))) (addtest (test-strong-2) test-1 (ensure (scan scanner "**hello**"))) (addtest (test-strong-2) test-1 (ensure (not (scan scanner "**hello *")))) (addtest (test-strong-2) test-1 (ensure-same (scan scanner "***hello***") (values 1 10 #(3) #(8)) :test #'equalp)) (addtest (test-strong-2) test-1 (ensure-same (scan scanner "*** hello there ***") (values 1 18 #(3) #(16)) :test #'equalp)) cl-markdown-20101006-darcs/unit-tests/package.lisp0000644000175000017500000000332211453110206020301 0ustar xachxach(in-package #:common-lisp-user) (defpackage #:cl-markdown-test (:use #:common-lisp #:lift #:metatilities #:cl-containers #:cl-ppcre #:cl-markdown #:trivial-shell #:metabang-bind) (:shadowing-import-from #:lift #:with-timeout) (:shadowing-import-from #:metatilities #:copy-file) (:import-from #:trivial-shell #:shell-command) (:import-from #:cl-markdown #:scan-lines-with-scanners #:atx-header-markup-class #:blockquote-stripper #:chunk-source #:line-could-be-link-reference-title-p #:line-indentation #:line-is-blockquote-p #:line-is-code-p #:line-is-empty-p #:line-is-horizontal-rule-p #:line-starts-with-bullet-p #:line-starts-with-number-p #:it-starts-with-block-level-html-p #:markdown #:one-tab-stripper #:remove-atx-header #:remove-bullet #:remove-number #:strippers #:reset #:handle-setext-headers #:lines #:indentation #:maybe-strip-line #:chunks #:markup-class #:paragraph? #:link-info #:id #:properties #:title #:header1 #:header2 #:header3 #:header4 #:header5 #:header6 #:*spaces-per-tab* #:*parsing-environment* #:system-relative-pathname)) cl-markdown-20101006-darcs/unit-tests/test-markdown.lisp0000644000175000017500000001003711453110206021506 0ustar xachxach(in-package #:cl-markdown-test) #| (run-tests :suite 'test-markdown) |# (deftestsuite cl-markdown-test () ()) (deftestsuite test-bracket-processing (cl-markdown-test) ()) (addtest (test-bracket-processing) donot-process-code (let ((text (nth-value 1 (markdown "{set-property a \"set 1\"} Paragraph 1 Code 1 {set-property a \"set 2\"} More code All done: a = {property a}" :stream nil)))) (ensure (search "a = set 1" text :test 'char=) :report "a set correctly") (ensure (search "set 2" text :test 'char=) :report "code not mangled"))) (addtest (test-bracket-processing) double-brackets-for-code (let ((text (nth-value 1 (markdown "{set-property a \"set 1\"} Paragraph 1 Code 1 {{set-property a \"set 2\"}} More code All done: a = {property a}" :stream nil)))) (ensure (search "a = set 2" text :test 'char=) :report "a set correctly") (ensure-null (search "a \"set 2" text :test 'char=) :report "code not mangled"))) ;;;; (deftestsuite nested-properties (cl-markdown-test) ((temporary-directory "/tmp/")) (:setup (with-new-file (out (relative-pathname temporary-directory "bandi-1.md")) (format out " {set-property slush \"1234-simple\"} ")))) (addtest (nested-properties) try-it (let ((text (nth-value 1 (markdown "a {set-property a \"alpha\"} b {set-property b \"{property a}\"} c {set-property c \"a is {property b}\"} {property b} # Title is {property c} Hi there" :stream nil)))) (ensure (search "Title is a is alpha" text :test 'char=)))) (addtest (nested-properties) works-with-included-documents-too (let ((text (nth-value 1 (markdown (concatenate 'string " {include " (namestring (relative-pathname temporary-directory "bandi-1.md")) "} a {set-property a \"this is {property slush} too\"} b {set-property b \"{property a}\"} c {set-property c \"a is {property a}\"} # Title is {property c} Hi there") :stream nil)))) (ensure (search "Title is a is this is 1234-simple" text :test 'char=)))) (defvar *last-document* nil) (defun shell-tidy (source) (bind (((:values result error status) (shell-command (format nil "tidy --show-body-only 1 --quiet 1 ~ --show-warnings 0") :input source))) (values result error status))) (defun shell-markdown (source) (bind (((:values result error status) (shell-command (format nil "markdown") :input source))) (values result error status))) (deftestsuite test-snippets (cl-markdown-test) () :equality-test #'string-equal (:function (check-html-output (source html) (ensure-same (shell-tidy (nth-value 1 (markdown source :stream nil :format :html))) (shell-tidy html) :test 'samep))) (:function (check-output (source) (ensure-same (bind (((:values doc text) (markdown source :stream nil :format :html))) (setf *last-document* doc) ;; just get the first value (values (shell-tidy text))) (shell-tidy (shell-markdown source)) :test (lambda (a b) (compare-line-by-line a b :key 'cl-markdown::strip-whitespace :test 'string-equal)))))) ;;;;; (deftestsuite no-markdown-in-inline-html (cl-markdown-test) () :equality-test #'string=) (addtest (no-markdown-in-inline-html) no-emphasis (ensure-same (remove-if 'whitespacep (nth-value 1 (markdown "Hi " :format :html :stream nil))) "

    Hi

    ")) ;;;;; (deftestsuite inline-html (cl-markdown-test) () :equality-test #'string=) (addtest (inline-html) do-not-encode (ensure-same (remove-if 'whitespacep (nth-value 1 (markdown "Hi there" :format :html :stream nil))) "

    Hithere

    ")) (addtest (inline-html) encode-in-code (ensure-same (remove-if 'whitespacep (nth-value 1 (markdown "Hi `there`" :format :html :stream nil))) "

    Hi<em>there</em>

    ")) cl-markdown-20101006-darcs/unit-tests/development-tests/0000755000175000017500000000000011453110206021477 5ustar xachxachcl-markdown-20101006-darcs/unit-tests/development-tests/paragraphs-1.text0000644000175000017500000000010711453110206024671 0ustar xachxachHello there my name is bob there is bunny over here ======== What not cl-markdown-20101006-darcs/unit-tests/development-tests/paragraphs-2.text0000644000175000017500000000014411453110206024673 0ustar xachxachthis is paragraph number one. this is paragraph number two. and this is paragraph number three.cl-markdown-20101006-darcs/unit-tests/test-snippets.lisp0000644000175000017500000001642511453110206021540 0ustar xachxach(in-package #:cl-markdown-test) #| Could use something like the form below to test the structure of the output and thereby differentiate between parsing problems and output problems (collect-elements (chunks d) :transform (lambda (chunk) (list (started-by chunk) (ended-by chunk) (level chunk) (markup-class chunk))) :filter (lambda (chunk) (not (ignore? chunk)))) |# (deftestsuite test-escapes (test-snippets) ()) (addtest (test-escapes) catch-markdown-ones (check-output "\\\\ \\` \\* \\_ \\[ \\] \\( \\) \\# \\. \\! \\>")) (addtest (test-escapes) catch-markdown-ones-2 (ensure-cases (var) '(("\\") ("`") ("*") ("_") ("[") ("]") ("(") (")") ("#") (".") ("!") (">")) (check-output (format nil "hi \\~a dude" var)))) (addtest (test-escapes :expected-failure "Problem in test suite... Markdown output is bad") catch-markdown-ones-< (check-output "\\<")) (addtest (test-escapes) code-and-escapes (check-output "`\\*hi\\*`")) (addtest (test-escapes) star-and-escapes (check-output "*\\*hi\\**")) (deftestsuite test-lists-and-paragraphs (test-snippets) ()) (addtest (test-lists-and-paragraphs) list-item-with-paragraph-1 (check-output " * List item with another paragraph and some code * Another item this ends the list and starts a paragraph.")) (addtest (test-lists-and-paragraphs) mingling-text-and-code (check-output " para code para code ")) #+(or) (markdown " * List item with another paragraph and some code * Another item this ends the list and starts a paragraph.") (addtest (test-lists-and-paragraphs) list-item-with-paragraph-2 (check-output " * List item and some code ")) (addtest (test-lists-and-paragraphs) list-item-with-paragraph-3 (check-output " * Another item paragraph ")) (addtest (test-lists-and-paragraphs) list-item-with-paragraph-4 (check-output " * Item 1 paragraph 1 * Item 2 paragraph 2 The end")) (addtest (test-lists-and-paragraphs :expected-failure "Markdown views treats the 1. as a *.") list-item-with-paragraph-5 (check-output " * Item 1 1. paragraph 1")) (addtest (test-lists-and-paragraphs) nested-lists-1 (check-output " * Item 1 * Item A")) ;;?? Paragraph logic reversed? (addtest (test-lists-and-paragraphs) nested-lists-2 (check-output " * Item 1 * Item A * Item 2")) (addtest (test-lists-and-paragraphs) nested-lists-3 (check-output " * a * b * c * d ")) (addtest (test-lists-and-paragraphs) nested-lists-4 (check-output " * a * b * c * d ")) (addtest (test-lists-and-paragraphs) nested-lists-with-hard-returns (check-output " * Item 1 is spunky * Item A ")) (addtest (test-lists-and-paragraphs) lists-and-code-1 (ensure-same (nth-value 1 (cl-markdown:markdown " * The select form rewrites... If we add another line. (select (?x) (q ?x !property node)) ")) (nth-value 1 (cl-markdown:markdown " * The select form rewrites... If we add another line. (select (?x) (q ?x !property node)) ")) :test 'string=)) (addtest (test-lists-and-paragraphs :expected-failure "paragraphs") lists-and-blockquote (check-output "paragraph 1 > ok * item 1 q2. I thiought I had this one ok")) ;;;;; (deftestsuite test-break (test-snippets) () (:tests (no-spaces (check-output "hello there")) (one-space (check-output "hello there")) (two-spaces (check-output "hello there")) (three-spaces (check-output "hello there")))) ;; NOTE: markdown doesn't add the
    unless there is content _after_ ;; line that ends with two spaces... (addtest (test-break) rest-of-line (check-output "this is **strong** ok?")) (addtest (test-break) rest-of-line-2 (check-output "this _is_ **strong** ok?")) (addtest (test-break :expected-failure "markdown doesn't add the
    unless there is content _after_ line that ends with two spaces...") rest-of-line-3 (check-output "this _is_ **strong** ")) ;;;;; (deftestsuite entity-snippets (test-snippets) ()) (addtest (entity-snippets) entity-check-1 (check-output "AT&T puts the amp in & >boss<")) (addtest (entity-snippets) entity-check-2 (check-output "The AT&T is AT & T, not AT&T or AT &T")) (addtest (entity-snippets) entity-check-3 (check-output " Never forget AT &T")) ;;;; (deftestsuite numbered-lists (test-snippets) ()) (addtest (numbered-lists) at-margin (check-output " 1. hi 2. there ")) (addtest (numbered-lists) indented (check-output " 1. hi 2. there ")) (addtest (numbered-lists) nospace (check-output " 1.hi 2.there ")) (addtest (numbered-lists :expected-failure "Looks like a markdown bug") nocontents (check-output " 1. 2. ")) ;;;; (deftestsuite test-horizontal-rules (test-snippets) ()) (addtest (test-horizontal-rules) horizontal-rules-1 (check-output "Here are some rules. I hope you like 'em. --- *** - - - ** ** ** _ _ _____ _ _ Did you like them?")) (addtest (test-horizontal-rules) horizontal-rules-2 (ensure (search "this is code" (nth-value 1 (markdown:markdown "Here is an example: this is code - - - - " :stream nil)) :test 'char=))) ;;;; (deftestsuite test-nested-lists (test-snippets) ()) (addtest (test-nested-lists) three-deep (check-output " * a * b * c")) (deftestsuite test-blockquotes (test-snippets) ()) (addtest (test-blockquotes) nested-1 (check-output " > a > b ")) (addtest (test-blockquotes) nested-2 (check-output " > a > b ")) (addtest (test-blockquotes) nested-3 (check-output " > a >> b ")) ;;;;; ;; test example from hhalvors@Princeton.EDU (addtest (test-snippets) header-paragraph-embedded-link (check-output "## Common Lisp * An item with a link [link](link.html) and some following text.")) ;; test example from hhalvors@Princeton.EDU (addtest (test-snippets) header-paragraph-embedded-link-in-list (check-output "## Common Lisp * An item with a link [link](link.html) and some following text. * Another item")) ;; test example from hhalvors@Princeton.EDU (addtest (test-snippets) headers-and-lists (check-output "## Common Lisp * An item with a link [link](link.html) and some following text. ## A second level heading * Another item")) (addtest (test-snippets) header-in-list (check-output "* ok # eh")) (addtest (test-snippets) reference-link-text-with-line-breaks (check-output "Hi this [is so][is-so] cool. [is-so]: http://a.c.c/")) #+(or) (markdown "* ok # eh") (addtest (test-snippets) list-item-with-hard-return (check-output "* A first list item with a hard return * A second list item ")) (addtest (test-snippets) list-items-and-paragraphs-1 (check-output "* first line second line")) (addtest (test-snippets) list-items-and-paragraphs-2 (check-output "* first line * second line")) (addtest (test-snippets) list-items-and-paragraphs-3 (check-output "* first line * second line")) (addtest (test-snippets) list-items-and-paragraphs-4 (check-output "* first line second line")) (addtest (test-snippets) inline-html-1 (check-output "`
    foo
    `")) (addtest (test-snippets) inline-html-2 (check-output "Simple block on one line:
    foo
    ")) cl-markdown-20101006-darcs/unit-tests/test-regexes.lisp0000644000175000017500000000523411453110206021331 0ustar xachxach(in-package #:cl-markdown-test) (deftestsuite test-regexes (cl-markdown-test-all) ()) (deftestsuite test-url (test-regexes) ()) (addtest (test-url) test-1 (ensure-same (scan-to-strings '(:sequence url) "My page is at http://www.metabang.com/~gwking/public.") (values "http://www.metabang.com/~gwking/public" #("www.metabang.com" "~gwking/public")) :test 'equalp)) ;;; --------------------------------------------------------------------------- (deftestsuite test-link-label (test-regexes) ()) (addtest (test-link-label) test-link (bind (((values nil registers) (scan-to-strings '(:sequence link-label) " [aa]: http://foo.bar"))) (ensure-same (aref registers 0) "aa") (ensure-same (aref registers 1) "http://foo.bar") (ensure-same (aref registers 2) nil))) (addtest (test-link-label) test-link-with-title (bind (((values nil registers) (scan-to-strings '(:sequence link-label) " [aa]: http://foo.bar \"best foos\""))) (ensure-same (aref registers 0) "aa") (ensure-same (aref registers 1) "http://foo.bar") (ensure-same (aref registers 2) "best foos"))) ;;; --------------------------------------------------------------------------- (deftestsuite test-inline-links (test-regexes) ()) (addtest (test-inline-links) test-1 (ensure-same (nth-value 1 (scan-to-strings '(:sequence inline-link) "This is an [in-line](http://www.google.com/ \"Link to Google\") link")) #("in-line" "http://www.google.com/" "Link to Google") :test 'equalp)) (addtest (test-inline-links) test-2 (ensure-same (nth-value 1 (scan-to-strings '(:sequence inline-link) "This is an [in-line](http://www.google.com/) link with no title")) #("in-line" "http://www.google.com/" nil) :test 'equalp)) (addtest (test-inline-links) test-2 (ensure-same (scan-to-strings '(:sequence inline-link) "This is not an (in-line)(http://www.google.com/) link with no title") nil)) ;;; --------------------------------------------------------------------------- (deftestsuite test-reference-links (test-regexes) ()) (addtest (test-reference-links) test-1 (ensure-same (nth-value 1 (scan-to-strings '(:sequence reference-link) "This is an [in-line][id] link")) #("in-line" "id") :test 'equalp)) (addtest (test-reference-links) test-2 (ensure-same (nth-value 1 (scan-to-strings '(:sequence reference-link) "This is an [in-line] [id] link with no title")) #("in-line" "id") :test 'equalp)) cl-markdown-20101006-darcs/unit-tests/test-chunkers.lisp0000644000175000017500000003145511453110206021515 0ustar xachxach(in-package #:cl-markdown-test) #| (run-tests :suite 'test-chunkers :break-on-errors? t) |# (deftestsuite test-it-starts-with-block-level-html-p (cl-markdown-test) () (:tests ((ensure (not (it-starts-with-block-level-html-p "")))) ((ensure (not (it-starts-with-block-level-html-p "
    ")))) ((ensure (not (it-starts-with-block-level-html-p "hello")))) ((ensure (not (it-starts-with-block-level-html-p "")))) ((ensure (not (it-starts-with-block-level-html-p "
    "))) ((ensure (it-starts-with-block-level-html-p "
    "))) ((ensure (it-starts-with-block-level-html-p "
      "))) ((ensure (not (it-starts-with-block-level-html-p "<>")))) ((ensure (not (it-starts-with-block-level-html-p "")))) ((ensure (not (it-starts-with-block-level-html-p "
    ")))) ((ensure (not (it-starts-with-block-level-html-p "/hello")))) ((ensure (not (it-starts-with-block-level-html-p "")))) ((ensure (not (it-starts-with-block-level-html-p "
    "))) ((ensure (it-starts-with-block-level-html-p ""))))) ;;;; (deftestsuite test-chunkers (cl-markdown-test) () (:equality-test 'samep)) (deftestsuite test-line-is-empty-p (test-chunkers) () (:test ((ensure (line-is-empty-p " ")))) (:test ((ensure (not (line-is-empty-p " 4"))))) (:test ((ensure (not (line-is-empty-p "4 "))))) (:test ((ensure (line-is-empty-p (coerce (list #\tab #\space #\newline) 'string)))))) (deftestsuite line-starts-with-number-p (test-chunkers) () (:test ((ensure-null (line-starts-with-number-p "1.")))) (:test ((ensure (not (line-starts-with-number-p "a."))))) (:test ((ensure (not (line-starts-with-number-p "1 hello"))))) (:test ((ensure-null (line-starts-with-number-p "10123.")))) (:test ((ensure (not (line-starts-with-number-p "10123th is big")))))) ;;; --------------------------------------------------------------------------- (deftestsuite line-starts-with-bullet-p (test-chunkers) () (:test ((ensure (line-starts-with-bullet-p "* hello")))) (:test ((ensure (not (line-starts-with-bullet-p " *."))))) (:test ((ensure (not (line-starts-with-bullet-p "*"))))) (:test ((ensure (line-starts-with-bullet-p " * ")))) (:test ((ensure (not (line-starts-with-bullet-p " *"))))) (:test ((ensure (not (line-starts-with-bullet-p " *")))))) ;;; --------------------------------------------------------------------------- (deftestsuite test-remove-bullet (test-chunkers) () (:test ((ensure-same (remove-bullet "* hello") "hello"))) (:test ((ensure-same (remove-bullet "*. hello") "hello"))) (:test ((ensure-same (remove-bullet "*. hello") "hello"))) (:test ((ensure-same (remove-bullet "* hello") "hello"))) (:test ((ensure-same (remove-bullet "+. hello") "hello"))) (:test ((ensure-same (remove-bullet "-. hello") "hello"))) ; (:test ((ensure-same (remove-bullet " -. hello") "hello"))) (:test ((ensure-same (remove-bullet "-. ") "")))) ;;; --------------------------------------------------------------------------- (deftestsuite test-remove-number (test-chunkers) () (:test ((ensure-same (remove-number "1. hello") "hello"))) (:test ((ensure-same (remove-number "232. hello") "hello"))) (:test ((ensure-same (remove-number "3. hello") "hello"))) (:test ((ensure-same (remove-number "453. hello") "hello"))) (:test ((ensure-same (remove-number "2.") ""))) (:test ((ensure-same (remove-number "123. ") "")))) ;;; --------------------------------------------------------------------------- (deftestsuite test-line-is-horizontal-rule-p (test-chunkers) () (:test ((ensure (line-is-horizontal-rule-p "---")))) (:test ((ensure (line-is-horizontal-rule-p "- - -")))) (:test ((ensure (line-is-horizontal-rule-p " - - - ")))) (:test ((ensure (line-is-horizontal-rule-p " - --")))) (:test ((ensure-null (line-is-horizontal-rule-p " = = =")))) (:test ((ensure (line-is-horizontal-rule-p "__ _")))) (:test ((ensure (not (line-is-horizontal-rule-p "-_-"))))) ) ;;; --------------------------------------------------------------------------- (deftestsuite test-atx-header-markup-class (test-chunkers) () (:test ((ensure-same (atx-header-markup-class "# hello #") 'header1))) (:test ((ensure-same (atx-header-markup-class "###### hello #") 'header6))) (:test ((ensure-error (atx-header-markup-class "####### hello #")))) (:test ((ensure-error (atx-header-markup-class "h###ello"))))) ;;; --------------------------------------------------------------------------- (deftestsuite test-remove-atx-header (test-chunkers) () (:test ((ensure-same (remove-atx-header "# hello #") "hello"))) (:test ((ensure-same (remove-atx-header "###### hello #") "hello"))) (:test ((ensure-same (remove-atx-header "### ### hello") "### hello")))) ;;; --------------------------------------------------------------------------- (deftestsuite test-line-indentation (test-chunkers) () (:test ((ensure-same (line-indentation " hello") 2))) (:test ((ensure-same (line-indentation "") 0))) (:test ((ensure-same (line-indentation "hello ") 0))) (:test ((ensure-same (line-indentation (coerce (list #\tab #\space #\h #\i) 'string)) (1+ *spaces-per-tab*))))) ;;; --------------------------------------------------------------------------- (deftestsuite test-line-is-code-p (test-chunkers) () (:test ((ensure (line-is-code-p " hello")))) (:test ((ensure (line-is-code-p (format nil "~Chello" #\Tab))))) (:test ((ensure (not (line-is-code-p "hello")))))) ;;; --------------------------------------------------------------------------- (deftestsuite test-line-is-blockquote-p (test-chunkers) () (:test ((ensure (line-is-blockquote-p "> hello")))) (:test ((ensure (line-is-blockquote-p " > hello")))) (:test ((ensure (line-is-blockquote-p " > hello")))) (:test ((ensure (line-is-blockquote-p " > hello")))) (:test ((ensure (not (line-is-blockquote-p " > hello")))))) ;;; --------------------------------------------------------------------------- (deftestsuite test-one-tab-stripper (test-chunkers) () (:test ((ensure-same (one-tab-stripper "hello") (values "hello" nil)))) (:test ((ensure-same (one-tab-stripper " hello") (values "hello" t)))) (:test ((ensure-same (one-tab-stripper " hello") (values " hello" t))))) ;;; --------------------------------------------------------------------------- (deftestsuite test-blockquote-stripper (test-chunkers) () (:test ((ensure-same (blockquote-stripper "hello") (values "hello" nil)))) (:test ((ensure-same (blockquote-stripper "> hello") (values "hello" t)))) (:test ((ensure-same (blockquote-stripper ">") (values "" t)))) (:test ((ensure-same (blockquote-stripper " > > why") (values "> why" t)))) (:test ((ensure-same (blockquote-stripper " > > why") (values " > why" t)))) (:test ((ensure-same (blockquote-stripper " >> why") (values " >> why" nil))))) (deftestsuite test-maybe-strip-line (test-chunkers) () (:setup (reset *parsing-environment*))) (addtest (test-maybe-strip-line) no-strippers (ensure-same (maybe-strip-line "hello") (values "hello" 0)) (ensure-same (maybe-strip-line " hello") (values " hello" 0))) ;;; --------------------------------------------------------------------------- (deftestsuite test-maybe-strip-line-one-tab-stripper (test-maybe-strip-line) () (:setup (insert-item (strippers *parsing-environment*) 'one-tab-stripper)) (:test ((ensure-same (maybe-strip-line "hello") (values "hello" 0)))) (:test ((ensure-same (maybe-strip-line " hello") (values "hello" 1)))) (:test ((ensure-same (maybe-strip-line " * hello") (values "* hello" 1)))) (:test ((ensure-same (maybe-strip-line " hello") (values " hello" 1))))) ;;; --------------------------------------------------------------------------- (deftestsuite test-maybe-strip-line-two-tab-strippers (test-maybe-strip-line) () (:setup (insert-item (strippers *parsing-environment*) 'one-tab-stripper) (insert-item (strippers *parsing-environment*) 'one-tab-stripper)) (:test ((ensure-same (maybe-strip-line "hello") (values "hello" 0)))) (:test ((ensure-same (maybe-strip-line " hello") (values "hello" 1)))) (:test ((ensure-same (maybe-strip-line " * hello") (values "* hello" 1)))) (:test ((ensure-same (maybe-strip-line " hello") (values "hello" 2)))) (:test ((ensure-same (maybe-strip-line " hello") (values " hello" 2))))) (deftestsuite test-maybe-strip-line-one-bq-strippers (test-maybe-strip-line) () (:setup (insert-item (strippers *parsing-environment*) 'blockquote-stripper)) (:test ((ensure-same (maybe-strip-line "hello") (values "hello" 0)))) (:test ((ensure-same (maybe-strip-line "> hello") (values "hello" 1)))) (:test ((ensure-same (maybe-strip-line ">> hello") (values "> hello" 1))))) (deftestsuite test-maybe-strip-line-two-bq-strippers (test-maybe-strip-line-one-bq-strippers) () (:setup (insert-item (strippers *parsing-environment*) 'blockquote-stripper)) (:test ((ensure-same (maybe-strip-line "hello") (values "hello" 0)))) (:test ((ensure-same (maybe-strip-line "> hello") (values "hello" 1)))) (:test ((ensure-same (maybe-strip-line ">> hello") (values "hello" 2))))) ;;?? FiXME -- why?! #+(or) (deftestsuite test-chunk-source (test-chunkers) ((document (progn (princ "******") (make-container 'cl-markdown::document))))) (deftestsuite test-chunk-source (test-chunkers) (document) (:setup (setf document (make-container 'cl-markdown::document)))) (addtest (test-chunk-source) simple-1 (chunk-source document "this is paragraph number one. this is paragraph number two. and this is paragraph number three.") (ensure-same (size (chunks document)) 3)) (addtest (test-chunk-source) simple-mixed-indenting-no-breaks (chunk-source document "this is paragraph number one. this is paragraph number one and this is paragraph number one") (ensure-same (size (chunks document)) 1)) (addtest (test-chunk-source) simple-bullets-with-breaks (chunk-source document "this is a list * item 1 * item 2 that's all.") (ensure-same (size (chunks document)) 4)) ;;; --------------------------------------------------------------------------- (addtest (test-chunk-source) simple-multiline-bullets (chunk-source document "this is a list * item 1 is a bullet that take many lines * item 2 that's all.") (ensure-same (size (chunks document)) 4)) ;;; --------------------------------------------------------------------------- (addtest (test-chunk-source) simple-multiline-bullets-with-breaks (chunk-source document "this is a list * item 1 is a bullet that take many lines over three paragraphs * item 2 that's all.") (ensure-same (size (chunks document)) 6) (ensure-same (size (lines (nth-element (chunks document) 0))) 1) (ensure-same (size (lines (nth-element (chunks document) 2))) 2) (ensure-same (indentation (nth-element (chunks document) 3)) 2) (ensure-same (indentation (nth-element (chunks document) 4)) 0)) ;;; --------------------------------------------------------------------------- (addtest (test-chunk-source) simple-bullets-and-numbers (chunk-source document "this is a list * of * bullets 1. and numbers 2. and more numbers + and then bullets - and more bullets that's all.") (ensure-same (size (chunks document)) 8)) ;;; --------------------------------------------------------------------------- (addtest (test-chunk-source) simple-headers-1 (handle-setext-headers (chunk-source document "Random line Title One ======== What not ======== Just some equal signs ")) (ensure-same (size (chunks document)) 4)) (addtest (test-chunk-source) simple-headers-2 (handle-setext-headers (chunk-source document " Title ======== Subtitle -------- What not is a good start to a paragraph. ")) (ensure-same (size (chunks document)) 3)) ;;; --------------------------------------------------------------------------- ;;; line-could-be-link-reference-title-p ;;; --------------------------------------------------------------------------- (deftestsuite line-could-be-link-reference-title-p (test-chunkers) () (:test ((ensure (line-could-be-link-reference-title-p " \"Hi\"")))) (:test ((ensure (line-could-be-link-reference-title-p "\"Hi\"")))) (:test ((ensure (not (line-could-be-link-reference-title-p " He said \"hi\""))))) (:test ((ensure (not (line-could-be-link-reference-title-p " \"no closing quote"))))))cl-markdown-20101006-darcs/COPYING0000644000175000017500000000212011453110206014724 0ustar xachxachCopyright (c) 2006 - 2007 Gary Warren King (gwking@metabang.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cl-markdown-20101006-darcs/cl-markdown-comparisons.asd0000644000175000017500000000153611453110206021145 0ustar xachxach(in-package #:common-lisp-user) (defpackage #:cl-markdown-test-system (:use #:cl #:asdf)) (in-package #:cl-markdown-test-system) (defsystem cl-markdown-comparisons :version "0.1" :author "Gary Warren King " :maintainer "Gary Warren King " :licence "MIT Style License" :components ((:module "unit-tests" :components ((:file "package") (:file "framework" :depends-on ("package")) (:file "comparison" :depends-on ("framework")) ))) :depends-on (:cl-markdown :lml2 :cl-html-diff :html-encode :trivial-shell ;; probably not needed if we rearranged more... :lift)) cl-markdown-20101006-darcs/cl-markdown-test.asd0000644000175000017500000000164011453110206017563 0ustar xachxach;;; -*- Mode: Lisp; package: CL-USER; Syntax: Common-lisp; Base: 10 -*- (in-package #:common-lisp-user) (defpackage #:cl-markdown-test-system (:use #:cl #:asdf)) (in-package #:cl-markdown-test-system) (defsystem cl-markdown-test :author "Gary Warren King " :maintainer "Gary Warren King " :licence "MIT Style License" :components ((:module "setup" :pathname "unit-tests/" :components ((:file "package") (:file "utilities" :depends-on ("package")) (:file "test-markdown" :depends-on ("package")))) (:module "unit-tests" :depends-on ("setup") :components ((:file "test-chunkers") (:file "test-snippets") (:file "test-links") (:file "test-brackets-and-includes") (:file "brackets-with-empty-lines") (:file "test-headers")))) :depends-on (:cl-markdown :lift :trivial-shell))