From 40e65192d58643079d923df196ed5eb4a1cce1fc Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Wed, 8 Nov 2017 00:03:48 -0500 Subject: [PATCH] Support exporting table column alignment markers The Org table alignment markers "", "", "" translate to the markers in Markdown tables for left, right, center column alignment as recognized by Blackfriday. Fixes https://github.com/kaushalmodi/ox-hugo/issues/95 --- ox-blackfriday.el | 99 +++++++++++++++++-- test/site/content-org/all-posts.org | 18 ++++ .../content/posts/table-column-alignment.md | 28 ++++++ 3 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 test/site/content/posts/table-column-alignment.md diff --git a/ox-blackfriday.el b/ox-blackfriday.el index 973c6d1..5be1bce 100644 --- a/ox-blackfriday.el +++ b/ox-blackfriday.el @@ -35,6 +35,7 @@ This check is specifically track if that horizontal rule was inserted after the first row of the table.") + ;;; User-Configurable Variables @@ -43,6 +44,7 @@ inserted after the first row of the table.") :tag "Org Export Blackfriday" :group 'org-export) + ;;; Define Back-End @@ -130,10 +132,8 @@ INFO is a plist used as a communication channel." "Return width of TABLE at given COLUMN using INFO. INFO is a plist used as communication channel. Width of a column -is determined either by inquiring -`org-blackfriday-width-cookies' in the column, or by the maximum -cell with in the column." - ;; (message "[ox-bf-table-col-width DBG] table: %S" table) +is determined either by inquiring `org-blackfriday-width-cookies' +in the column, or by the maximum cell with in the column." (let ((cookie (when (hash-table-p org-blackfriday-width-cookies) (gethash column org-blackfriday-width-cookies)))) (if (and (eq table org-blackfriday-width-cookies-table) @@ -192,6 +192,61 @@ Returns nil immediately if PLAIN-LIST is not an ordered list." ;; (message "has custom counter: %S" has-custom-counter) has-custom-counter)) +;;;; Table Cell Alignment +;; Below function is heavily adapted from +;; `org-export-table-cell-alignment' from ox.el. The main difference +;; is that the below variation can return a `default' value too. +(defun org-blackfriday-table-cell-alignment (table-cell info) + "Return TABLE-CELL contents alignment. + +INFO is a plist used as the communication channel. + +Return alignment as specified by the last alignment cookie in the +same column as TABLE-CELL. If no such cookie is found, return +`default'. Possible values are `default', `left', `right' and +`center'." + (let* ((row (org-export-get-parent table-cell)) + (table (org-export-get-parent row)) + (cells (org-element-contents row)) + (columns (length cells)) + (column (- columns (length (memq table-cell cells)))) + (cache (or (plist-get info :table-cell-alignment-cache) + (let ((table (make-hash-table :test #'eq))) + (plist-put info :table-cell-alignment-cache table) + table))) + (align-vector (or (gethash table cache) + (puthash table (make-vector columns nil) cache)))) + (or (aref align-vector column) + (let (cookie-align) + (dolist (row (org-element-contents (org-export-get-parent row))) + (cond + ;; In a special row, try to find an alignment cookie at + ;; COLUMN. + ((org-export-table-row-is-special-p row info) + (let ((value (org-element-contents + (elt (org-element-contents row) column)))) + ;; Since VALUE is a secondary string, the following + ;; checks avoid useless expansion through + ;; `org-export-data'. + (when (and value + (not (cdr value)) + (stringp (car value)) + (string-match "\\`<\\([lrc]\\)?\\([0-9]+\\)?>\\'" + (car value)) + (match-string 1 (car value))) + (setq cookie-align (match-string 1 (car value)))))) + ;; Ignore table rules. + ((eq (org-element-property :type row) 'rule)))) + ;; Return value. Alignment specified by cookies has + ;; precedence over alignment deduced from cell's contents. + (aset align-vector + column + (cond ((equal cookie-align "l") 'left) + ((equal cookie-align "r") 'right) + ((equal cookie-align "c") 'center) + (t 'default))))))) + + ;;; Transcode Functions @@ -363,6 +418,7 @@ CONTENTS contains the text with strike-through markup." CONTENTS is content of the cell. INFO is a plist used as a communication channel." + ;; (message "[ox-bf-table-cell DBG]") ;; (message "[ox-bf-table-cell DBG] In contents: %s" contents) (let* ((table (org-export-get-parent-table table-cell)) (column (cdr (org-export-table-cell-address table-cell info))) @@ -375,6 +431,10 @@ communication channel." (make-string (max 0 (- width (string-width data))) ?\s) right-border)) (cell-width (length cell))) + ;; Just calling `org-blackfriday-table-cell-alignment' will save + ;; the alignment info for the current cell/column to the INFO + ;; channel.. magic! + (org-blackfriday-table-cell-alignment table-cell info) ;; Each cell needs to be at least 3 characters wide (4 chars, ;; including the table border char "|"); otherwise the export ;; is not rendered as a table @@ -389,6 +449,7 @@ communication channel." CONTENTS is cell contents of TABLE-ROW. INFO is a plist used as a communication channel." + ;; (message "[ox-bf-table-row DBG]") (let* ((table (org-export-get-parent-table table-row)) (row-num (cl-position ;Begins with 0 table-row @@ -417,8 +478,34 @@ communication channel." ;; under it for Blackfriday to detect the whole object as a table. (when (and (stringp row) (null org-blackfriday--hrule-inserted)) - (let ((rule (replace-regexp-in-string "[^|]" "-" row))) - (setq row (concat row "\n" rule)) + ;; (message "[ox-bf-table-row DBG] row: %s" row) + (let ((rule (replace-regexp-in-string "[^|]" "-" row)) + (pos 0) + (new-rule "") + matches) + ;; (message "[ox-bf-table-row DBG] rule: %s" rule) + ;; https://emacs.stackexchange.com/a/7150/115 + (while (string-match "|-+" rule pos) + (push (match-string 0 rule) matches) + (setq pos (match-end 0))) + (setq matches (nreverse matches)) + ;; Get the align-vector that was saved in the INFO channel in + ;; `org-blackfriday-table-cell-alignment'. + (let* ((alignment-cache (plist-get info :table-cell-alignment-cache)) + (align-vector (gethash table alignment-cache)) + (col 0)) + ;; (message "[ox-bf-table-row DBG] align-vector: %S" align-vector) + (dolist (match matches) + (let ((align (aref align-vector col))) + (when (member align '(left center)) + (setq match (replace-regexp-in-string "\\`|-" "|:" match))) + (when (member align '(right center)) + (setq match (replace-regexp-in-string "-\\'" ":" match)))) + (setq new-rule (concat new-rule match)) + (setq col (1+ col)))) + (setq new-rule (concat new-rule "|")) + ;; (message "[ox-bf-table-row DBG] new-rule: %s" new-rule) + (setq row (concat row "\n" new-rule)) (setq org-blackfriday--hrule-inserted t))) ;; (message "[ox-bf-table-row DBG] Row:\n%s" row) row)) diff --git a/test/site/content-org/all-posts.org b/test/site/content-org/all-posts.org index 7774c17..1126107 100644 --- a/test/site/content-org/all-posts.org +++ b/test/site/content-org/all-posts.org @@ -439,6 +439,24 @@ Here is the second post on Emacs. :EXPORT_FILE_NAME: table-with-single-row :END: | a | b | +** Table column alignment +:PROPERTIES: +:EXPORT_FILE_NAME: table-column-alignment +:END: +*** Table with 3 rows +| | | | | +| Right | Left | No | Center | +| Long Content To | Spread Out the Width | Alignment | Of the Table To See Alignment | +| Right | Left | Marker | Center | +*** Table with 2 rows +| | | | | +| Left | Default | Center | Right | +| 1 | 2 | 3 | 4 | +*** Table with 1 row +/A table with just one row, with alignment markers doesn't make +sense. But hey, a test is a test./ +| | | | | +| Left | Default | Center | Right | * Source blocks :src_block: ** Example text with code blocks :ARCHIVE: :PROPERTIES: diff --git a/test/site/content/posts/table-column-alignment.md b/test/site/content/posts/table-column-alignment.md new file mode 100644 index 0000000..92dc825 --- /dev/null +++ b/test/site/content/posts/table-column-alignment.md @@ -0,0 +1,28 @@ ++++ +title = "Table column alignment" +tags = ["table"] +draft = false ++++ + +## Table with 3 rows {#table-with-3-rows} + +| Right | Left | No | Center | +|----------------:|:---------------------|-----------|:-----------------------------:| +| Long Content To | Spread Out the Width | Alignment | Of the Table To See Alignment | +| Right | Left | Marker | Center | + + +## Table with 2 rows {#table-with-2-rows} + +| Left | Default | Center | Right | +|:-----|---------|:------:|------:| +| 1 | 2 | 3 | 4 | + + +## Table with 1 row {#table-with-1-row} + +_A table with just one row, with alignment markers doesn't make +sense. But hey, a test is a test._ + +| Left | Default | Center | Right | +|:-----|---------|:------:|------:|