org-e-ascii: Use new table structure

* EXPERIMENTAL/org-e-ascii.el (org-e-ascii-table): Use new table
  structure.
(org-e-ascii-table--column-width,
org-e-ascii-table--vertical-separators,
org-e-ascii-table--format-cell, org-e-ascii-table--build-hline):
Remove functions.
(org-e-ascii-table-cell, org-e-ascii-table-row,
org-e-ascii--table-cell-width): New functions.
This commit is contained in:
Nicolas Goaziou 2012-04-21 09:52:08 +02:00 committed by Jambunathan K
parent 01d8153f72
commit 851fe42608
1 changed files with 97 additions and 226 deletions

View File

@ -1583,253 +1583,124 @@ contextual information."
;;;; Table
;; While `org-e-ascii-table' is the callback function expected by
;; org-export mechanism, it requires four subroutines to display
;; tables accordingly to chosen charset, alignment and width
;; specifications.
;; Thus, `org-e-ascii-table--column-width' computes the display width
;; for each column in the table,
;; `org-e-ascii-table--vertical-separators' returns a vector
;; containing separators (or lack thereof),
;; `org-e-ascii-table--build-hline' creates various hline strings,
;; depending on charset, separators and position within the tabl and
;; `org-e-ascii-table--format-cell' properly aligns contents within
;; a given cell and width.
(defun org-e-ascii-table (table contents info)
"Transcode a TABLE element from Org to ASCII.
CONTENTS is nil. INFO is a plist holding contextual information."
(let ((raw-table (org-element-property :raw-table table))
(caption (org-e-ascii--build-caption table info)))
(let ((caption (org-e-ascii--build-caption table info)))
(concat
;; Possibly add a caption string above.
(when (and caption org-e-ascii-caption-above) (concat caption "\n"))
;; Insert table. Note: "table.el" tables are left unmodified.
(if (eq (org-element-property :type table) 'table.el) raw-table
(let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))
;; Extract information out of the raw table (TABLE-INFO)
;; and clean it (CLEAN-TABLE).
(table-info (org-export-table-format-info raw-table))
(special-col-p (plist-get table-info :special-column-p))
(alignment (plist-get table-info :alignment))
(clean-table (org-export-clean-table raw-table special-col-p))
;; Change table into lisp, much like
;; `org-table-to-lisp', though cells are parsed and
;; transcoded along the way.
(lisp-table
(mapcar
(lambda (line)
(if (string-match org-table-hline-regexp line) 'hline
(mapcar
(lambda (cell)
(org-trim
(org-export-secondary-string
(org-element-parse-secondary-string
cell
(cdr (assq 'item org-element-string-restrictions)))
'e-ascii info)))
(org-split-string (org-trim line) "\\s-?|\\s-?"))))
(org-split-string clean-table "[ \t]*\n[ \t]*")))
;; Compute real column widths.
(column-widths
(org-e-ascii-table--column-width lisp-table table-info))
;; Construct separators according to column groups.
(separators (org-e-ascii-table--vertical-separators table-info))
;; Build different `hline' strings, depending on
;; separators, column widths and position.
(hline-standard
(org-e-ascii-table--build-hline
nil separators column-widths info))
(hline-top
(and utf8p (org-e-ascii-table--build-hline
'top separators column-widths info)))
(hline-bottom
(and utf8p (org-e-ascii-table--build-hline
'bottom separators column-widths info))))
;; Now build table back, with correct alignment, considering
;; columns widths and separators.
(mapconcat
(lambda (line)
(cond
((eq line 'hline) hline-standard)
((eq line 'hline-bottom) hline-bottom)
((eq line 'hline-top) hline-top)
(t (loop for cell in line
for col from 0 to (length line)
concat
(concat
(let ((sep (aref separators col)))
(if (and utf8p (not (string= sep ""))) "" sep))
(org-e-ascii-table--format-cell
cell col column-widths alignment info)) into l
finally return
(concat l
(let ((sep (aref separators col)))
(if (and utf8p (not (string= sep ""))) ""
sep)))))))
;; If charset is `utf-8', make sure lisp-table always starts
;; with `hline-top' and ends with `hline-bottom'.
(if (not utf8p) lisp-table
(setq lisp-table
(cons 'hline-top
(if (eq (car lisp-table) 'hline) (cdr lisp-table)
lisp-table)))
(setq lisp-table
(nconc
(if (eq (car (last lisp-table)) 'hline) (butlast lisp-table)
lisp-table)
'(hline-bottom)))) "\n")))
(if (eq (org-element-property :type table) 'org) contents
(org-element-property :value table))
;; Possible add a caption string below.
(when (and caption (not org-e-ascii-caption-above))
(concat "\n" caption)))))
(defun org-e-ascii-table--column-width (table table-info)
"Return vector of TABLE columns width.
TABLE is the Lisp representation of the Org table considered.
TABLE-INFO holds information about the table. See
`org-export-table-format-info'.
;;;; Table Cell
Unlike to `:width' property from `org-export-table-format-info',
the return value is a vector containing width of every column,
not only those with an explicit width cookie. Special column, if
any, is ignored."
;; All rows have the same length, but be sure to ignore hlines.
(let ((width (make-vector
(loop for row in table
unless (eq row 'hline)
return (length row))
0)))
;; Set column width to the maximum width of the cells in that
;; column.
(mapc
(lambda (line)
(let ((idx 0))
(unless (eq line 'hline)
(mapc (lambda (cell)
(let ((len (length cell)))
(when (> len (aref width idx)) (aset width idx len)))
(incf idx))
line))))
table)
(unless org-e-ascii-table-widen-columns
;; When colums are not widened, width cookies have precedence
;; over string lengths. Thus, overwrite the latter with the
;; former.
(let ((cookies (plist-get table-info :width))
(specialp (plist-get table-info :special-column-p)))
;; Remove special column from COOKIES vector, if any.
(loop for w across (if specialp (substring cookies 1) cookies)
for idx from 0 to width
when w do (aset width idx w))))
;; Return value.
width))
(defun org-e-ascii-table--vertical-separators (table-info)
"Return a vector of strings for vertical separators.
(defun org-e-ascii--table-cell-width (table-cell info)
"Return width of TABLE-CELL.
TABLE-INFO holds information about considered table. See
`org-export-table-format-info'.
Width of a cell is determined either by a width cookie in the
same column as the cell, or by the length of its contents.
Return value is a vector whose length is one more than the number
of columns in the table. Special column, if any, is ignored."
(let* ((colgroups (plist-get table-info :column-groups))
(separators (make-vector (1+ (length colgroups)) "")))
(if org-e-ascii-table-keep-all-vertical-lines
(make-vector (length separators) "|")
(let ((column 0))
(mapc (lambda (group)
(when (memq group '(start start-end))
(aset separators column "|"))
(when (memq group '(end start-end))
(aset separators (1+ column) "|"))
(incf column))
colgroups)
;; Remove unneeded special column.
(if (not (plist-get table-info :special-column-p)) separators
(substring separators 1))))))
When `org-e-ascii-table-widen-columns' is non-nil, width cookies
are ignored. "
(or (and (not org-e-ascii-table-widen-columns)
(org-export-table-cell-width table-cell info))
(let* ((max-width 0)
(table (org-export-get-parent-table table-cell info))
(specialp (org-export-table-has-special-column-p table))
(col (cdr (org-export-table-cell-address table-cell info))))
(org-element-map
table 'table-row
(lambda (row)
(setq max-width
(max (length
(org-export-data
(elt (if specialp (car (org-element-contents row))
(org-element-contents row))
col)
(plist-get info :back-end) info))
max-width))))
max-width)))
(defun org-e-ascii-table--format-cell (cell col width alignment info)
"Format CELL with column width and alignment constraints.
CELL is the contents of the cell, as a string.
COL is the column containing the cell considered.
WIDTH is a vector holding every column width, as returned by
`org-e-ascii-table--column-width'.
ALIGNMENT is a vector containing alignment strings for every
column.
INFO is a plist used as a communication channel."
(let ((col-width (if org-e-ascii-table-widen-columns (aref width col)
(or (aref width col) (length cell)))))
;; When CELL is too large, it has to be truncated.
(unless (or org-e-ascii-table-widen-columns (<= (length cell) col-width))
(setq cell (concat (substring cell 0 (- col-width 2)) "=>")))
(defun org-e-ascii-table-cell (table-cell contents info)
"Transcode a TABLE-CELL object from Org to ASCII.
CONTENTS is the cell contents. INFO is a plist used as
a communication channel."
;; Determine column width. When `org-e-ascii-table-widen-columns'
;; is nil and some width cookie has set it, use that value.
;; Otherwise, compute the maximum width among transcoded data of
;; each cell in the column.
(let ((width (org-e-ascii--table-cell-width table-cell info)))
;; When contents are too large, truncate them.
(unless (or org-e-ascii-table-widen-columns (<= (length contents) width))
(setq contents (concat (substring contents 0 (- width 2)) "=>")))
;; Align contents correctly within the cell.
(let* ((indent-tabs-mode nil)
(align (aref alignment col))
(aligned-cell
(org-e-ascii--justify-string
(org-trim cell) col-width
(cond ((string= align "c") 'center)
((string= align "r") 'right)))))
;; Return aligned cell, with missing white spaces added and
;; space separators between columns.
(format
" %s "
(concat aligned-cell
(make-string (- col-width (length aligned-cell)) ? ))))))
(data
(when contents
(org-e-ascii--justify-string
contents width
(org-export-table-cell-alignment table-cell info)))))
(setq contents (concat data (make-string (- width (length data)) ? ))))
;; Return cell.
(concat (format " %s " contents)
(when (memq 'right (org-export-table-cell-borders table-cell info))
(if (eq (plist-get info :ascii-charset) 'utf-8) "" "|")))))
(defun org-e-ascii-table--build-hline (position separators column-widths info)
"Return string used as an horizontal line in tables.
POSITION is a symbol among `top', `bottom' and nil, which
specifies position of the horizontal line within the table.
;;;; Table Row
SEPARATORS is a vector strings specifying separators used in the
table, as returned by `org-e-ascii-table--vertical-separators'.
COLUMN-WIDTHS is a vector of numbers specifying widths of all
columns in the table, as returned by
`org-e-ascii-table--column-width'.
INFO is a plist used as a communication channel."
(let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
(loop for idx from 0 to (length separators)
for width across column-widths
concat
(concat
(cond ((string= (aref separators idx) "") nil)
((and utf8p (zerop idx))
(cond ((eq position 'top) "")
((eq position 'bottom) "")
(t "")))
(utf8p
(cond ((eq position 'top) "")
((eq position 'bottom) "")
(t "")))
(t "+"))
;; Hline has to cover all the cell and both white spaces
;; between columns.
(make-string (+ width 2)
(cond ((not utf8p) ?-)
((not position) ?─)
(t ?━))))
into hline
finally return
;; There is one separator more than columns, so handle it
;; here.
(concat
hline
(cond
((string= (aref separators idx) "") nil)
(utf8p (cond ((eq position 'top) "")
((eq position 'bottom) "")
(t "")))
(t "+"))))))
(defun org-e-ascii-table-row (table-row contents info)
"Transcode a TABLE-ROW element from Org to ASCII.
CONTENTS is the row contents. INFO is a plist used as
a communication channel."
(when (eq (org-element-property :type table-row) 'standard)
(let ((build-hline
(function
(lambda (lcorner horiz vert rcorner)
(concat
(apply
'concat
(org-element-map
table-row 'table-cell
(lambda (cell)
(let ((width (org-e-ascii--table-cell-width cell info))
(borders (org-export-table-cell-borders cell info)))
(concat
(when (and (memq 'left borders)
(equal (org-element-map
table-row 'table-cell 'identity info t)
cell)))
(make-string (+ 2 width) (string-to-char horiz))
(cond
((not (memq 'right borders)) nil)
((equal (car (last (org-element-contents table-row)))
cell)
rcorner)
(t vert)))))
info)) "\n"))))
(utf8p (eq (plist-get info :ascii-charset) 'utf-8))
(borders (org-export-table-cell-borders
(org-element-map table-row 'table-cell 'identity info t)
info)))
(concat (cond
((and (memq 'top borders) (or utf8p (memq 'above borders)))
(if utf8p (funcall build-hline "" "" "" "")
(funcall build-hline "+" "-" "+" "+")))
((memq 'above borders)
(if utf8p (funcall build-hline "" "" "" "")
(funcall build-hline "+" "-" "+" "+"))))
(when (memq 'left borders) (if utf8p "" "|"))
contents "\n"
(when (and (memq 'bottom borders) (or utf8p (memq 'below borders)))
(if utf8p (funcall build-hline "" "" "" "")
(funcall build-hline "+" "-" "+" "+")))))))
;;;; Target