Repository navigation
Support media file assets #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1d4b476
97b2576
5ed12cf
6edeff0
3548fb5
e6d5856
01d64a4
3fee325
8d93075
d361500
c32a2a6
9573eb9
b10fa0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| (uiop:define-package #:40ants-doc-full/assets | ||
| (:use #:cl) | ||
| (:import-from #:40ants-doc-full/commondoc/image | ||
| #:local-image) | ||
| (:import-from #:40ants-doc-full/commondoc/mapper | ||
| #:map-nodes) | ||
| (:import-from #:40ants-doc-full/commondoc/xref | ||
| #:xref | ||
| #:xref-locative | ||
| #:xref-symbol) | ||
| (:export #:defasset)) | ||
| (in-package #:40ants-doc-full/assets) | ||
|
|
||
|
|
||
| (defclass asset () | ||
| ((name :initarg :name | ||
| :reader asset-name) | ||
| (source :initarg :source | ||
| :reader asset-source) | ||
| (target-filename :initarg :target-filename | ||
| :reader asset-target-filename) | ||
| (description :initarg :description | ||
| :reader asset-description) | ||
| (width :initarg :width | ||
| :initform nil | ||
| :reader asset-width) | ||
| (height :initarg :height | ||
| :initform nil | ||
| :reader asset-height))) | ||
|
|
||
|
|
||
| (defun make-asset (name source target-filename description &rest restargs | ||
| &key width height) | ||
| (declare (ignore width height)) | ||
| (apply #'make-instance 'asset | ||
| :name name | ||
| :source source | ||
| :target-filename target-filename | ||
| :description description | ||
| restargs)) | ||
|
|
||
|
|
||
| (defvar *assets* (make-hash-table :test #'eq)) | ||
|
|
||
|
|
||
| (defun asset-symbol-p (symbol) | ||
| (not (null (gethash symbol *assets*)))) | ||
|
|
||
|
|
||
| (defmethod 40ants-doc::transform-symbol-entry :around ((entry symbol)) | ||
| (or (gethash entry *assets*) | ||
| (call-next-method))) | ||
|
|
||
|
|
||
| (defmethod 40ants-doc/object-package::object-package ((asset asset)) | ||
| (symbol-package (asset-name asset))) | ||
|
|
||
|
|
||
| (defun pathname-designator-string (pathname-designator) | ||
| (namestring (pathname pathname-designator))) | ||
|
|
||
|
|
||
| (defun register-asset (name source &key target-filename description width height) | ||
| (check-type name symbol) | ||
| (check-type source (or string pathname)) | ||
|
|
||
| (let* ((source (pathname-designator-string source)) | ||
| (target-filename (pathname-designator-string | ||
| (or target-filename source))) | ||
| (description (or description | ||
| (symbol-name name))) | ||
| (new-asset (make-asset name source target-filename description | ||
| :width width | ||
| :height height)) | ||
| (old-asset (gethash name *assets*))) | ||
| (when (and old-asset | ||
| (not (and (string= (asset-source old-asset) | ||
| (asset-source new-asset)) | ||
| (string= (asset-target-filename old-asset) | ||
| (asset-target-filename new-asset)) | ||
| (string= (asset-description old-asset) | ||
| (asset-description new-asset)) | ||
| (equal (asset-width old-asset) | ||
| (asset-width new-asset)) | ||
| (equal (asset-height old-asset) | ||
| (asset-height new-asset))))) | ||
| (error "Asset ~S is already registered with different properties." | ||
| name)) | ||
| (setf (gethash name *assets*) new-asset) | ||
| new-asset)) | ||
|
|
||
|
|
||
| (defmacro defasset (name source &rest args) | ||
| "Register NAME as a local image used in documentation. | ||
|
|
||
| SOURCE is a pathname designator. By default, the file is copied to the | ||
| same relative pathname below the documentation output directory. Use | ||
| :TARGET-FILENAME to choose another relative output pathname. | ||
|
|
||
| Every unqualified occurrence of NAME in prose is rendered as an image." | ||
| (check-type name symbol) | ||
| `(eval-when (:compile-toplevel :load-toplevel :execute) | ||
| (register-asset ',name ,source ,@args))) | ||
|
|
||
|
|
||
| (defun safe-target-filename-p (target-filename) | ||
| (let ((pathname (pathname target-filename))) | ||
| (and (not (uiop:absolute-pathname-p pathname)) | ||
| (not (member :up (pathname-directory pathname)))))) | ||
|
|
||
|
|
||
| (defun validate-asset (asset) | ||
| (unless (probe-file | ||
| (40ants-doc-full/commondoc/image::resolve-local-image-path | ||
| (asset-source asset))) | ||
| (error "Asset ~S source file does not exist: ~A" | ||
| (asset-name asset) | ||
| (asset-source asset))) | ||
|
|
||
| (unless (safe-target-filename-p (asset-target-filename asset)) | ||
| (error "Asset ~S target filename is not below the output directory: ~A" | ||
| (asset-name asset) | ||
| (asset-target-filename asset))) | ||
|
|
||
| (maphash (lambda (name other-asset) | ||
| (when (and (not (eq name (asset-name asset))) | ||
| (string= (asset-target-filename other-asset) | ||
| (asset-target-filename asset))) | ||
|
Comment on lines
+125
to
+128
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For every asset occurrence, AGENTS.md reference: AGENTS.md:L9-L11 Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not think this is a big issue right now. |
||
| (error "Assets ~S and ~S use the same target filename: ~A" | ||
| (asset-name asset) | ||
| name | ||
| (asset-target-filename asset)))) | ||
| *assets*) | ||
| asset) | ||
|
|
||
|
|
||
| (defun asset-to-local-image (asset) | ||
| (validate-asset asset) | ||
| (local-image (asset-source asset) | ||
| :target-filename (asset-target-filename asset) | ||
| :description (asset-description asset) | ||
| :width (asset-width asset) | ||
| :height (asset-height asset))) | ||
|
|
||
|
|
||
| (defun replace-assets (document) | ||
| "Replace XREF nodes naming registered assets with local image nodes." | ||
| (map-nodes document | ||
| (lambda (node) | ||
| (if (and (typep node 'xref) | ||
| (null (xref-locative node))) | ||
| (let ((asset (gethash (xref-symbol node) *assets*))) | ||
| (if asset | ||
| (asset-to-local-image asset) | ||
| node)) | ||
| node)))) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
SOURCEis an absolute pathname—such as the common result ofasdf:system-relative-pathname—and:target-filenameis omitted, this assigns that absolute namestring as the target.validate-assetthen rejects it unconditionally, despite the public API describing:target-filenameas optional and promising a source-derived relative target. Derive the default with the existing relative-path logic or require an explicit target for sources that cannot be relativized.AGENTS.md reference: AGENTS.md:L9-L11
Useful? React with 👍 / 👎.