;;; mst-point.el --- bullet points in text mode. ;; Author: Mark Triggs ;; This file 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 2, or (at your option) ;; any later version. ;; This file 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 GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; *Disgusting* code for inserting and manipulating bullet points. ;; M-x mst-point inserts a dot point if there isn't one already. If there is ;; already a dot point, it cycles through different possible indentations. ;; Obviously `mst-point' should be bound to a key. ;;; Code: (defun mst-point-indent-to-level (level) (let ((mark (point-marker))) (unless (and (looking-at "$") (looking-back "[*-] ")) (destructuring-bind (start end) (mst-point-find-bounds) (unless (and start end) (error "Couldn't find the bounds of this point")) (replace-regexp "\\(\n\\|^ +\\)" "" nil start end))) (beginning-of-line) (delete-horizontal-space) (let ((indent (* (1- (* 2 level)) 2))) (insert (make-string indent ?\ )) (delete-char 1) (if (oddp level) (insert "*") (insert "-"))) (let ((old-prefix fill-prefix)) (fill-region (line-beginning-position) (line-end-position)) (setq fill-prefix old-prefix)) (goto-char (marker-position mark)))) (defun mst-last-level () (or (save-excursion (let ((start (car (mst-point-find-bounds)))) (and start (goto-char start) (progn (back-to-indentation) (mst-point-spaces-to-level (current-column)))))) (mst-point-parent-level) 2)) (defun mst-point-find-bounds () (let ((start nil) (end nil)) (save-excursion (back-to-indentation) (while (and (not (looking-at "[*-]")) (not (bobp)) (not (zerop (current-column)))) (previous-line 1) (back-to-indentation)) (when (looking-at "[*-]") (setq start (line-beginning-position))) (when start ;; Find the end (forward-char 2) (let ((goal (current-column))) (while (and (not (save-excursion (end-of-line) (eobp))) (= (current-column) goal) (looking-back "[ *-]+")) (next-line 1) (back-to-indentation)) (if (and (save-excursion (end-of-line) (eobp)) (= (current-column) goal) (looking-back "[ *-]+")) (setq end (point-max)) (previous-line 1) (end-of-line) (setq end (point)))) (while (or (looking-back " ") (looking-back "^")) (setq end (1- end)) (goto-char end))) (list start end)))) (defun mst-point () (interactive) (cond ((let ((bounds (mst-point-find-bounds))) (and (every 'identity bounds) (<= (car bounds) (point)) (<= (point) (1+ (cadr bounds))))) (let ((parent (mst-point-parent-level))) (cond ((not parent) nil) ((> (mst-last-level) 1) (mst-point-indent-to-level (1- (mst-last-level)))) ((= (mst-last-level) 1) (mst-point-indent-to-level (1+ parent)))) (when (and (looking-back "[-*]") (looking-at "$")) (insert " ")))) (t (unless (save-excursion (beginning-of-line) (looking-at "^ *$")) (end-of-line) (newline)) (delete-horizontal-space) (insert "* ") (let ((parent (mst-point-parent-level))) (if parent (mst-point-indent-to-level parent) (mst-point-indent-to-level 1)) (insert " "))))) (defun mst-point-spaces-to-level (spaces) (/ (1+ (/ spaces 2)) 2)) (defun mst-point-parent-level () (save-excursion (destructuring-bind (start end) (mst-point-find-bounds) (cond (start (goto-char start) (next-line -1) (catch 'finished (while (not (bobp)) (save-excursion (beginning-of-line) (cond ((looking-at "^\\( +\\)[*-]") (throw 'finished (mst-point-spaces-to-level (length (match-string 1))))) ((and (not (looking-at "^$")) (looking-at "^[^ ]")) (throw 'finished nil)) (t nil))) (next-line -1)))) (t (next-line -1) (catch 'finished (while (not (bobp)) (back-to-indentation) (cond ((zerop (current-column)) (throw 'finished nil)) ((every 'identity (mst-point-find-bounds)) (mst-point-parent-level)) (t (next-line -1)))))))))) (provide 'mst-point) ;;; mst-point.el ends here