Skip to content

Instantly share code, notes, and snippets.

@tonyg
Last active September 6, 2023 21:02
Show Gist options
  • Save tonyg/c951be93656b45027ed7d15e79f07c01 to your computer and use it in GitHub Desktop.
Save tonyg/c951be93656b45027ed7d15e79f07c01 to your computer and use it in GitHub Desktop.
;;; markdown-join.el --- Join markdown tables based on cell contents -*- lexical-binding: t; -*-
;; Copyright (C) 2023 Tony Garnock-Jones
;; Author: Tony Garnock-Jones <tonyg@leastfixedpoint.com>
;; Keywords: data, convenience, matching
;; Version: 0.0.1
;; Package-Requires: ((markdown-mode "2.6") (emacs "25.1"))
;; Homepage: https://gist.github.com/tonyg/c951be93656b45027ed7d15e79f07c01
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; When working on Markdown files containing tables, occasionally one will wish to bring in
;; information from another table nearby. This package offers one way to accomplish this, if
;; the source table is in a separate buffer: use `M-x markdown-join`.
;;; Code:
(require 'markdown-mode)
(defun markdown-join-get-row ()
"Retrieves all cells in the row at point."
;; Cribs from markdown-table-get-column and markdown-table-get-cell.
(let ((acc '()))
(save-excursion
(beginning-of-line)
(while (search-forward "|" (line-end-position) t)
(when (and (not (looking-back "\\\\|" (line-beginning-position)))
(not (markdown--thing-at-wiki-link (match-beginning 0))))
(setq acc (cons (progn
(skip-chars-backward "^|\n") (backward-char 1)
(if (looking-at "|[^|\r\n]*")
(let* ((pos (match-beginning 0))
(val (buffer-substring (1+ pos) (match-end 0))))
(goto-char (min (line-end-position) (+ 2 pos)))
;; Trim whitespaces
(setq val (replace-regexp-in-string "\\`[ \t]+" "" val)
val (replace-regexp-in-string "[ \t]+\\'" "" val)))
(forward-char 1) ""))
acc)))))
(reverse acc)))
;;;###autoload
(defun markdown-join (source-buffer)
"Copies cells from another Markdown table into the row in the table at point.
This function requires point to be in a cell in a table.
Requests a buffer containing the table to pull values
from (SOURCE-BUFFER). Point in that buffer should be in a cell of
a Markdown table.
The contents of the cell under point in the current (``target'')
buffer is compared with cells in the column selected by point in
SOURCE-BUFFER. The first matching row is copied, cell by cell, to
the row at point in the target buffer, excepting the column under
point in SOURCE-BUFFER (since its contents are the same as the
cell under point in the target).
To join two whole tables, try using a keyboard macro to repeat
markdown-join for each row in the target table."
(interactive "bBuffer containing table to pull values from: ")
(save-excursion
(let ((target-buffer (current-buffer)))
(unless (with-current-buffer target-buffer (markdown-table-at-point-p))
(user-error "Please select a target markdown table and column in %s" target-buffer))
(let* ((target-join-value (markdown--remove-invisible-markup (markdown-table-get-cell)))
(source-columns
(with-current-buffer source-buffer
(unless (markdown-table-at-point-p)
(user-error "Please select a source markdown table and column in %s" (current-buffer)))
(let ((source-column (markdown-table-get-column)))
(save-restriction
;; Select current hline-separated table region, else the whole table
(let ((start (markdown-table-begin))
(end (markdown-table-end)))
(narrow-to-region
(save-excursion
(if (re-search-backward
markdown-table-hline-regexp start t)
(line-beginning-position 2)
start))
(if (save-excursion (re-search-forward
markdown-table-hline-regexp end t))
(match-beginning 0)
end)))
;; Find a matching row
(let ((source-columns
(with-temp-message "Searching for matching row..."
(catch 'join-successful
(save-excursion
(goto-char (markdown-table-begin))
(while (re-search-forward markdown-table-dline-regexp (point-max) t)
(when (string-equal (markdown-table-get-cell source-column) target-join-value)
(throw 'join-successful (markdown-join-get-row)))))))))
(when source-columns
(append (seq-take source-columns (1- source-column))
(seq-drop source-columns source-column)))))))))
(if source-columns
(progn (save-excursion
(end-of-line)
(dolist (v source-columns)
(insert " ")
(insert v)
(insert " |")))
"Join successful.")
"No matching row found.")))))
(provide 'markdown-join)
;;; markdown-join.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment