12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604 |
- ;;; org-list.el --- Plain lists for Org-mode
- ;;
- ;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
- ;; Free Software Foundation, Inc.
- ;;
- ;; Author: Carsten Dominik <carsten at orgmode dot org>
- ;; Bastien Guerry <bzg AT altern DOT org>
- ;; Keywords: outlines, hypermedia, calendar, wp
- ;; Homepage: http://orgmode.org
- ;; Version: 7.01trans
- ;;
- ;; This file is part of GNU Emacs.
- ;;
- ;; GNU Emacs 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.
- ;; GNU Emacs 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. If not, see <http://www.gnu.org/licenses/>.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;;; Commentary:
- ;; This file contains the code dealing with plain lists in Org-mode.
- ;;; Code:
- (eval-when-compile
- (require 'cl))
- (require 'org-macs)
- (require 'org-compat)
- (defvar org-blank-before-new-entry)
- (defvar org-M-RET-may-split-line)
- (defvar org-complex-heading-regexp)
- (defvar org-odd-levels-only)
- (declare-function org-invisible-p "org" ())
- (declare-function org-on-heading-p "org" (&optional invisible-ok))
- (declare-function outline-next-heading "outline" ())
- (declare-function org-back-to-heading "org" (&optional invisible-ok))
- (declare-function org-back-over-empty-lines "org" ())
- (declare-function org-skip-whitespace "org" ())
- (declare-function org-trim "org" (s))
- (declare-function org-get-indentation "org" (&optional line))
- (declare-function org-timer-item "org-timer" (&optional arg))
- (declare-function org-combine-plists "org" (&rest plists))
- (declare-function org-entry-get "org"
- (pom property &optional inherit literal-nil))
- (declare-function org-narrow-to-subtree "org" ())
- (declare-function org-show-subtree "org" ())
- (defgroup org-plain-lists nil
- "Options concerning plain lists in Org-mode."
- :tag "Org Plain lists"
- :group 'org-structure)
- (defcustom org-cycle-include-plain-lists t
- "When t, make TAB cycle visibility on plain list items.
- Cycling plain lists works only when the cursor is on a plain list
- item. When the cursor is on an outline heading, plain lists are
- treated as text. This is the most stable way of handling this,
- which is why it is the default.
- When this is the symbol `integrate', then during cycling, plain
- list items will *temporarily* be interpreted as outline headlines
- with a level given by 1000+i where i is the indentation of the
- bullet. This setting can lead to strange effects when switching
- visibility to `children', because the first \"child\" in a
- subtree decides what children should be listed. If that first
- \"child\" is a plain list item with an implied large level
- number, all true children and grand children of the outline
- heading will be exposed in a children' view."
- :group 'org-plain-lists
- :type '(choice
- (const :tag "Never" nil)
- (const :tag "With cursor in plain list (recommended)" t)
- (const :tag "As children of outline headings" integrate)))
- (defcustom org-list-demote-modify-bullet nil
- "Default bullet type installed when demoting an item.
- This is an association list, for each bullet type, this alist will point
- to the bullet that should be used when this item is demoted.
- For example,
- (setq org-list-demote-modify-bullet
- '((\"+\" . \"-\") (\"-\" . \"+\") (\"*\" . \"+\")))
- will make
- + Movies
- + Silence of the Lambs
- + My Cousin Vinny
- + Books
- + The Hunt for Red October
- + The Road to Omaha
- into
- + Movies
- - Silence of the Lambs
- - My Cousin Vinny
- + Books
- - The Hunt for Red October
- - The Road to Omaha"
- :group 'org-plain-lists
- :type '(repeat
- (cons
- (choice :tag "If the current bullet is "
- (const "-")
- (const "+")
- (const "*")
- (const "1.")
- (const "1)"))
- (choice :tag "demotion will change it to"
- (const "-")
- (const "+")
- (const "*")
- (const "1.")
- (const "1)")))))
- (defcustom org-plain-list-ordered-item-terminator t
- "The character that makes a line with leading number an ordered list item.
- Valid values are ?. and ?\). To get both terminators, use t. While
- ?. may look nicer, it creates the danger that a line with leading
- number may be incorrectly interpreted as an item. ?\) therefore is
- the safe choice."
- :group 'org-plain-lists
- :type '(choice (const :tag "dot like in \"2.\"" ?.)
- (const :tag "paren like in \"2)\"" ?\))
- (const :tab "both" t)))
- (defcustom org-list-two-spaces-after-bullet-regexp nil
- "A regular expression matching bullets that should have 2 spaces after them.
- When nil, no bullet will have two spaces after them.
- When a string, it will be used as a regular expression. When the
- bullet type of a list is changed, the new bullet type will be
- matched against this regexp. If it matches, there will be two
- spaces instead of one after the bullet in each item of the list."
- :group 'org-plain-lists
- :type '(choice
- (const :tag "never" nil)
- (regexp)))
- (defcustom org-empty-line-terminates-plain-lists nil
- "Non-nil means an empty line ends all plain list levels.
- Otherwise, look for `org-list-end-regexp'."
- :group 'org-plain-lists
- :type 'boolean)
- (defcustom org-list-end-regexp "^[ \t]*\n[ \t]*\n"
- "Regexp matching the end of all plain list levels.
- It must start with \"^\" and end with \"\\n\". It defaults to 2
- blank lines. `org-empty-line-terminates-plain-lists' has
- precedence over it."
- :group 'org-plain-lists
- :type 'string)
- (defcustom org-auto-renumber-ordered-lists t
- "Non-nil means automatically renumber ordered plain lists.
- Renumbering happens when the sequence have been changed with
- \\[org-shiftmetaup] or \\[org-shiftmetadown]. After other editing
- commands, use \\[org-ctrl-c-ctrl-c] to trigger renumbering."
- :group 'org-plain-lists
- :type 'boolean)
- (defcustom org-provide-checkbox-statistics t
- "Non-nil means update checkbox statistics after insert and toggle.
- When this is set, checkbox statistics is updated each time you
- either insert a new checkbox with \\[org-insert-todo-heading] or
- toggle a checkbox with \\[org-ctrl-c-ctrl-c]."
- :group 'org-plain-lists
- :type 'boolean)
- (defcustom org-hierarchical-checkbox-statistics t
- "Non-nil means checkbox statistics counts only the state of direct children.
- When nil, all boxes below the cookie are counted.
- This can be set to nil on a per-node basis using a COOKIE_DATA property
- with the word \"recursive\" in the value."
- :group 'org-plain-lists
- :type 'boolean)
- (defcustom org-description-max-indent 20
- "Maximum indentation for the second line of a description list.
- When the indentation would be larger than this, it will become
- 5 characters instead."
- :group 'org-plain-lists
- :type 'integer)
- (defcustom org-list-radio-list-templates
- '((latex-mode "% BEGIN RECEIVE ORGLST %n
- % END RECEIVE ORGLST %n
- \\begin{comment}
- #+ORGLST: SEND %n org-list-to-latex
- -
- \\end{comment}\n")
- (texinfo-mode "@c BEGIN RECEIVE ORGLST %n
- @c END RECEIVE ORGLST %n
- @ignore
- #+ORGLST: SEND %n org-list-to-texinfo
- -
- @end ignore\n")
- (html-mode "<!-- BEGIN RECEIVE ORGLST %n -->
- <!-- END RECEIVE ORGLST %n -->
- <!--
- #+ORGLST: SEND %n org-list-to-html
- -
- -->\n"))
- "Templates for radio lists in different major modes.
- All occurrences of %n in a template will be replaced with the name of the
- list, obtained by prompting the user."
- :group 'org-plain-lists
- :type '(repeat
- (list (symbol :tag "Major mode")
- (string :tag "Format"))))
- ;;; Internal functions
- (defun org-list-end-re ()
- "Return the regex corresponding to the end of a list.
- It depends on `org-empty-line-terminates-plain-lists'."
- (if org-empty-line-terminates-plain-lists
- "^[ \t]*\n"
- org-list-end-regexp))
- (defun org-item-re (&optional general)
- "Return the correct regular expression for plain lists.
- If GENERAL is non-nil, return the general regexp independent of the value
- of `org-plain-list-ordered-item-terminator'."
- (cond
- ((or general (eq org-plain-list-ordered-item-terminator t))
- "\\([ \t]*\\([-+]\\|\\([0-9]+[.)]\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
- ((= org-plain-list-ordered-item-terminator ?.)
- "\\([ \t]*\\([-+]\\|\\([0-9]+\\.\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
- ((= org-plain-list-ordered-item-terminator ?\))
- "\\([ \t]*\\([-+]\\|\\([0-9]+)\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
- (t (error "Invalid value of `org-plain-list-ordered-item-terminator'"))))
- (defun org-list-terminator-between (min max &optional firstp)
- "Find the position of a list ender between MIN and MAX, or nil.
- This function looks for `org-list-end-re' not matching a block.
- If FIRSTP in non-nil, return the point at the beginning of the
- nearest valid terminator from min. Otherwise, return the point at
- the end of the nearest terminator from max."
- (save-excursion
- (let* ((start (if firstp min max))
- (end (if firstp max min))
- (search-fun (if firstp
- #'org-search-forward-unenclosed
- #'org-search-backward-unenclosed))
- (list-end-p (progn
- (goto-char start)
- (funcall search-fun (org-list-end-re) end t))))
- ;; Is there a valid list terminator somewhere ?
- (and list-end-p
- ;; we want to be on the first line of the list ender
- (match-beginning 0)))))
- (defun org-search-unenclosed-internal (search-fun regexp bound noerror count)
- "Search for REGEXP with SEARCH-FUN but don't stop inside blocks or at protected places."
- (let ((origin (point)))
- (cond
- ;; nothing found: return nil
- ((not (funcall search-fun regexp bound noerror count)) nil)
- ;; match is enclosed or protected: start again, searching one
- ;; more occurrence away.
- ((or (save-match-data
- (org-in-regexps-block-p "^[ \t]*#\\+\\(begin\\|BEGIN\\)_\\([a-zA-Z0-9_]+\\)"
- '(concat "^[ \t]*#\\+\\(end\\|END\\)_" (match-string 2))))
- (get-text-property (match-beginning 0) 'org-protected))
- (goto-char origin)
- (org-search-unenclosed-internal search-fun regexp bound noerror (1+ count)))
- ;; else return point.
- (t (point)))))
- (defun org-search-backward-unenclosed (regexp &optional bound noerror)
- "Like `re-search-backward' but don't stop inside blocks or at protected places."
- (org-search-unenclosed-internal #'re-search-backward regexp bound noerror 1))
- (defun org-search-forward-unenclosed (regexp &optional bound noerror)
- "Like `re-search-forward' but don't stop inside blocks or at protected places."
- (org-search-unenclosed-internal #'re-search-forward regexp bound noerror 1))
- (defun org-get-item-same-level-internal (search-fun pos limit pre-move)
- "Return point at the beginning of next item at the same level.
- Search items using function SEARCH-FUN, from POS to LIMIT. It
- uses PRE-MOVE before searches. Return nil if no item was found.
- Internal use only. Prefer `org-get-next-item' and
- `org-get-previous-item' for cleaner code."
- (save-excursion
- (when pos (goto-char pos))
- (let ((begin (point))
- (ind (progn
- (org-beginning-of-item)
- (org-get-indentation)))
- (start (point-at-bol)))
- ;; we don't want to match the current line.
- (funcall pre-move)
- ;; Skip any sublist on the way
- (while (and (funcall search-fun (org-item-re) limit t)
- (> (org-get-indentation) ind))
- (funcall pre-move))
- (when (and (/= (point-at-bol) start) ; Have we moved ?
- (= (org-get-indentation) ind))
- (point-at-bol)))))
- (defun org-insert-item-internal (pos &optional checkbox after-bullet)
- "Insert a new item in a list.
- If POS is before first character after bullet of the item, the
- new item will be created before the current one.
- Insert a checkbox if CHECKBOX is non-nil, and string AFTER-BULLET
- after the bullet. Cursor will be after this text once the
- function ends."
- (goto-char pos)
- ;; Point in a special block: move before it prior to add a new item.
- (when (org-in-regexps-block-p
- "^[ \t]*#\\+\\(begin\\|BEGIN\\)_\\([a-zA-Z0-9_]+\\)"
- '(concat "^[ \t]*#\\+\\(end\\|END\\)_" (match-string 2)))
- ;; in case we're on the #+begin line
- (end-of-line)
- (re-search-backward "^[ \t]*#\\+\\(begin\\|BEGIN\\)_" nil t)
- (end-of-line 0))
- (let* ((true-pos (point))
- (bullet (and (org-beginning-of-item)
- (looking-at (org-item-re))
- (match-string 0)))
- (before-p (progn
- ;; Descriptive list: text starts after colons.
- (or (looking-at ".*::[ \t]+")
- ;; At a checkbox: text starts after it.
- (org-at-item-checkbox-p)
- ;; Otherwise, text starts after bullet.
- (org-at-item-p))
- (<= true-pos (match-end 0))))
- ;; Guess number of blank lines used to separate items.
- (blank-lines-nb
- (let ((insert-blank-p
- (cdr (assq 'plain-list-item org-blank-before-new-entry))))
- (cond
- ((or
- org-empty-line-terminates-plain-lists
- (not insert-blank-p))
- 0)
- ((eq insert-blank-p t) 1)
- ;; plain-list-item is 'auto. Count blank lines separating
- ;; neighbours items in list.
- (t
- (let ((next-p (org-get-next-item (point) (org-list-bottom-point))))
- (cond
- ;; Is there a next item?
- (next-p (goto-char next-p)
- (org-back-over-empty-lines))
- ;; Is there a previous item?
- ((not (org-first-list-item-p)) (org-back-over-empty-lines))
- ;; no luck: item is alone. Use default value.
- (t 1)))))))
- (insert-fun
- (lambda (text)
- ;; insert bullet above item in order to avoid bothering
- ;; with possible blank lines ending last item.
- (org-beginning-of-item)
- (insert (concat bullet
- (when checkbox "[ ] ")
- after-bullet))
- ;; Stay between after-bullet and before text.
- (save-excursion
- (insert (concat text (make-string (1+ blank-lines-nb) ?\n))))
- (unless before-p (org-move-item-down))
- (when checkbox (org-update-checkbox-count-maybe)))))
- (goto-char true-pos)
- (cond
- (before-p
- (funcall insert-fun nil)
- ;; Not taking advantage of renumbering while moving down. Need
- ;; to call it directly.
- (org-maybe-renumber-ordered-list) t)
- ;; Can't split item: insert bullet at the end of item.
- ((not (org-get-alist-option org-M-RET-may-split-line 'item))
- (funcall insert-fun nil) t)
- ;; else, insert a new bullet along with everything from point
- ;; down to last non-blank line of item.
- (t
- (delete-horizontal-space)
- ;; Get pos again in case previous command modified line.
- (let* ((pos (point))
- (end-before-blank (org-end-of-item-before-blank))
- (after-text
- (when (< pos end-before-blank)
- (prog1
- (buffer-substring pos end-before-blank)
- (delete-region pos end-before-blank)
- ;; delete any blank line at and before point.
- (beginning-of-line)
- (while (looking-at "^[ \t]*$")
- (delete-region (point-at-bol) (1+ (point-at-eol)))
- (beginning-of-line 0))))))
- (funcall insert-fun after-text) t)))))
- ;;; Predicates
- (defun org-in-item-p ()
- "Is the cursor inside a plain list ?"
- (save-excursion
- ;; Move to eol so that current line can be matched by
- ;; `org-item-re'.
- (let* ((limit (or (save-excursion (outline-previous-heading)) (point-min)))
- (actual-pos (goto-char (point-at-eol)))
- (last-item-start (save-excursion
- (org-search-backward-unenclosed (org-item-re) limit t)))
- (list-ender (org-list-terminator-between last-item-start actual-pos)))
- ;; We are in a list when we are on an item line or we can find
- ;; an item before and there is no valid list ender between us
- ;; and the item found.
- (and last-item-start
- (not list-ender)))))
- (defun org-first-list-item-p ()
- "Is this heading the first item in a plain list?"
- (unless (org-at-item-p)
- (error "Not at a plain list item"))
- (save-excursion
- (= (save-excursion (org-beginning-of-item)) (org-beginning-of-item-list))))
- (defun org-at-item-p ()
- "Is point in a line starting a hand-formatted item?"
- (save-excursion
- (goto-char (point-at-bol))
- (looking-at (org-item-re))))
- (defun org-at-item-bullet-p ()
- "Is point at the bullet of a plain list item?"
- (and (org-at-item-p)
- (not (member (char-after) '(?\ ?\t)))
- (< (point) (match-end 0))))
- (defun org-at-item-timer-p ()
- "Is point at a line starting a plain list item with a timer?
- This skips checkboxes, if any."
- (and (or (org-at-item-checkbox-p)
- (org-at-item-p))
- (save-excursion
- (goto-char (match-end 0))
- (skip-chars-forward " \t")
- (looking-at "\\([0-9]+:[0-9]+:[0-9]+\\)[ \t]+::[ \t]+"))))
- (defun org-at-item-checkbox-p ()
- "Is point at a line starting a plain-list item with a checklet?"
- (and (org-at-item-p)
- (save-excursion
- (goto-char (match-end 0))
- (skip-chars-forward " \t")
- (looking-at "\\[[- X]\\]"))))
- (defun org-checkbox-blocked-p ()
- "Is the current checkbox blocked from for being checked now?
- A checkbox is blocked if all of the following conditions are fulfilled:
- 1. The checkbox is not checked already.
- 2. The current entry has the ORDERED property set.
- 3. There is an unchecked checkbox in this entry before the current line."
- (catch 'exit
- (save-match-data
- (save-excursion
- (unless (org-at-item-checkbox-p) (throw 'exit nil))
- (when (equal (match-string 0) "[X]")
- ;; the box is already checked!
- (throw 'exit nil))
- (let ((end (point-at-bol)))
- (condition-case nil (org-back-to-heading t)
- (error (throw 'exit nil)))
- (unless (org-entry-get nil "ORDERED") (throw 'exit nil))
- (when (org-search-forward-unenclosed "^[ \t]*[-+*0-9.)] \\[[- ]\\]" end t)
- (org-current-line)))))))
- ;;; Navigate
- (defun org-list-top-point ()
- "Return point at the top level item in a list, or nil if not in a list."
- (save-excursion
- (and (org-in-item-p)
- (let ((pos (point-at-eol))
- (bound (or (outline-previous-heading) (point-min))))
- ;; Is there some list above this one ? If so, go to its ending.
- ;; Otherwise, go back to the heading above or bob.
- (goto-char (or (org-list-terminator-between bound pos) bound))
- ;; From there, search down our list.
- (org-search-forward-unenclosed (org-item-re) pos t)
- (point-at-bol)))))
- (defun org-list-bottom-point ()
- "Return point just before list ending or nil if not in a list."
- (save-excursion
- (and (org-in-item-p)
- (let ((pos (org-beginning-of-item))
- (bound (or (and (let ((outline-regexp org-outline-regexp))
- ;; Use default regexp because folding
- ;; changes OUTLINE-REGEXP.
- (outline-next-heading))
- (skip-chars-backward " \t\r\n")
- (1+ (point-at-eol)))
- (point-max))))
- ;; The list ending is either first point matching
- ;; `org-list-end-re', point at first white-line before next
- ;; heading, or eob.
- (or (org-list-terminator-between pos bound t) bound)))))
- (defun org-beginning-of-item ()
- "Go to the beginning of the current hand-formatted item.
- If the cursor is not in an item, throw an error. Return point."
- (interactive)
- (if (org-in-item-p)
- (if (org-at-item-p)
- (progn (beginning-of-line 1)
- (point))
- (org-search-backward-unenclosed (org-item-re) nil t)
- (goto-char (point-at-bol)))
- (error "Not in an item")))
- (defun org-end-of-item ()
- "Go to the end of the current hand-formatted item.
- If the cursor is not in an item, throw an error."
- (interactive)
- (let ((next-p (org-get-next-item (point) (org-list-bottom-point))))
- (cond ((not (org-in-item-p))
- (error "Not in an item"))
- (next-p
- (goto-char next-p))
- (t
- (org-end-of-item-list)))))
- (defun org-end-of-item-text-before-children ()
- "Move to the end of the item text, stops before the first child if any.
- Assumes that the cursor is in the first line of an item."
- (let ((limit (org-list-bottom-point)))
- (end-of-line)
- (goto-char
- (if (org-search-forward-unenclosed (org-item-re) limit t)
- (point-at-bol)
- limit))))
- (defun org-end-of-item-before-blank ()
- "Return point at end of item, before any blank line.
- Point returned is at eol."
- (save-excursion
- (org-end-of-item)
- (skip-chars-backward " \r\t\n")
- (point-at-eol)))
- (defun org-get-next-item (pos limit)
- "Get the point of the next item at the same level as POS.
- Stop searching at LIMIT. Return nil if no item is found. This
- function does not move point."
- (org-get-item-same-level-internal
- #'org-search-forward-unenclosed
- pos
- limit
- #'end-of-line))
- (defun org-get-previous-item (pos limit)
- "Get the point of the previous item at the same level as POS.
- Stop searching at LIMIT. Return nil if no item is found. This
- function does not move point."
- (org-get-item-same-level-internal
- #'org-search-backward-unenclosed
- pos
- limit
- #'beginning-of-line))
- (defun org-next-item ()
- "Move to the beginning of the next item.
- Item is at the same level in the current plain list. Error if not
- in a plain list, or if this is the last item in the list."
- (interactive)
- (let ((next-p (org-get-next-item (point) (org-list-bottom-point))))
- (if next-p
- (goto-char next-p)
- (error "On last item"))))
- (defun org-previous-item ()
- "Move to the beginning of the previous item.
- Item is at the same level in the current plain list. Error if not
- in a plain list, or if this is the first item in the list."
- (interactive)
- (let ((prev-p (org-get-previous-item (point) (org-list-top-point))))
- (if prev-p
- (goto-char prev-p)
- (error "On first item"))))
- (defun org-beginning-of-item-list ()
- "Go to the beginning item of the current list or sublist.
- Return point."
- (interactive)
- (let ((limit (org-list-top-point))
- (move-up (lambda (pos bound)
- ;; prev-p: any item of same level before ?
- (let ((prev-p (org-get-previous-item pos bound)))
- ;; recurse until no more item of the same level
- ;; can be found.
- (if prev-p
- (funcall move-up prev-p bound)
- pos)))))
- ;; Go to the last item found and at bol in case we didn't move
- (goto-char (funcall move-up (point) limit))
- (goto-char (point-at-bol))))
- (defun org-end-of-item-list ()
- "Go to the end of the current list or sublist.
- Return point."
- (interactive)
- (org-beginning-of-item)
- (let ((limit (org-list-bottom-point))
- (ind (org-get-indentation))
- (get-last-item (lambda (pos bound)
- ;; next-p: any item of same level after ?
- (let ((next-p (org-get-next-item pos bound)))
- ;; recurse until no more item of the same level
- ;; can be found.
- (if next-p
- (funcall get-last-item next-p bound)
- pos)))))
- ;; Move to the last item of every list or sublist encountered, and
- ;; down to bol of a higher-level item, or limit.
- (while (and (/= (point) limit)
- (>= (org-get-indentation) ind))
- (goto-char (funcall get-last-item (point) limit))
- (end-of-line)
- (when (org-search-forward-unenclosed (org-item-re) limit 'move)
- (beginning-of-line)))
- (point)))
- ;;; Manipulate
- (defun org-list-exchange-items (beg-A beg-B)
- "Swap item starting at BEG-A with item starting at BEG-B.
- Blank lines at the end of items are left in place. Assumes
- BEG-A is lesser than BEG-B."
- (save-excursion
- (let* ((end-of-item-no-blank (lambda (pos)
- (goto-char pos)
- (goto-char (org-end-of-item-before-blank))))
- (end-A-no-blank (funcall end-of-item-no-blank beg-A))
- (end-B-no-blank (funcall end-of-item-no-blank beg-B))
- (body-A (buffer-substring beg-A end-A-no-blank))
- (body-B (buffer-substring beg-B end-B-no-blank))
- (between-A-no-blank-and-B (buffer-substring end-A-no-blank beg-B)))
- (goto-char beg-A)
- (delete-region beg-A end-B-no-blank)
- (insert (concat body-B between-A-no-blank-and-B body-A)))))
- (defun org-move-item-down ()
- "Move the plain list item at point down, i.e. swap with following item.
- Subitems (items with larger indentation) are considered part of the item,
- so this really moves item trees."
- (interactive)
- (let ((pos (point))
- (col (current-column))
- (actual-item (org-beginning-of-item))
- (next-item (org-get-next-item (point) (save-excursion (org-end-of-item-list)))))
- (if (not next-item)
- (progn
- (goto-char pos)
- (error "Cannot move this item further down"))
- (org-list-exchange-items actual-item next-item)
- (org-maybe-renumber-ordered-list)
- (org-next-item)
- (move-to-column col))))
- (defun org-move-item-up ()
- "Move the plain list item at point up, i.e. swap with previous item.
- Subitems (items with larger indentation) are considered part of the item,
- so this really moves item trees."
- (interactive)
- (let ((pos (point))
- (col (current-column))
- (actual-item (org-beginning-of-item))
- (prev-item (org-get-previous-item (point) (save-excursion (org-beginning-of-item-list)))))
- (if (not prev-item)
- (progn
- (goto-char pos)
- (error "Cannot move this item further up"))
- (org-list-exchange-items prev-item actual-item)
- (org-maybe-renumber-ordered-list)
- (move-to-column col))))
- (defun org-insert-item (&optional checkbox)
- "Insert a new item at the current level.
- If cursor is before first character after bullet of the item, the
- new item will be created before the current one. Return t when
- things worked, nil when we are not in an item, or item is
- invisible."
- (unless (or (not (org-in-item-p))
- (org-invisible-p))
- (if (save-excursion
- (org-beginning-of-item)
- (org-at-item-timer-p))
- ;; Timer list: delegate to `org-timer-item'.
- (progn (org-timer-item) t)
- ;; if we're in a description list, ask for the new term.
- (let ((desc-text (when (save-excursion
- (and (org-beginning-of-item)
- (looking-at "[ \t]*\\(.*?\\) ::")
- (match-string 1)))
- (concat (read-string "Term: ") " :: "))))
- (org-insert-item-internal (point) checkbox desc-text)))))
- ;;; Indentation
- (defun org-get-string-indentation (s)
- "What indentation has S due to SPACE and TAB at the beginning of the string?"
- (let ((n -1) (i 0) (w tab-width) c)
- (catch 'exit
- (while (< (setq n (1+ n)) (length s))
- (setq c (aref s n))
- (cond ((= c ?\ ) (setq i (1+ i)))
- ((= c ?\t) (setq i (* (/ (+ w i) w) w)))
- (t (throw 'exit t)))))
- i))
- (defvar org-suppress-item-indentation) ; dynamically scoped parameter
- (defun org-shift-item-indentation (delta)
- "Shift the indentation in current item by DELTA."
- (unless (org-bound-and-true-p org-suppress-item-indentation)
- (save-excursion
- (let ((beg (point-at-bol))
- (end (progn (org-end-of-item) (point)))
- i)
- (goto-char end)
- (beginning-of-line 0)
- (while (> (point) beg)
- (when (looking-at "[ \t]*\\S-")
- ;; this is not an empty line
- (setq i (org-get-indentation))
- (if (and (> i 0) (> (setq i (+ i delta)) 0))
- (indent-line-to i)))
- (beginning-of-line 0))))))
- (defvar org-last-indent-begin-marker (make-marker))
- (defvar org-last-indent-end-marker (make-marker))
- (defun org-outdent-item (arg)
- "Outdent a local list item, but not its children."
- (interactive "p")
- (org-indent-item-tree (- arg) 'no-subtree))
- (defun org-indent-item (arg)
- "Indent a local list item, but not its children."
- (interactive "p")
- (org-indent-item-tree arg 'no-subtree))
- (defun org-outdent-item-tree (arg &optional no-subtree)
- "Outdent a local list item including its children.
- If NO-SUBTREE is set, only outdent the item itself, not its children."
- (interactive "p")
- (org-indent-item-tree (- arg) no-subtree))
- (defun org-indent-item-tree (arg &optional no-subtree)
- "Indent a local list item including its children.
- If NO-SUBTREE is set, only indent the item itself, not its children."
- (interactive "p")
- (and (org-region-active-p) (org-cursor-to-region-beginning))
- (unless (org-at-item-p)
- (error "Not on an item"))
- (let ((origin-ind (save-excursion
- (goto-char (org-list-top-point))
- (org-get-indentation)))
- beg end ind ind1 ind-bul delta ind-down ind-up firstp)
- (setq firstp (org-first-list-item-p))
- (save-excursion
- (setq end (and (org-region-active-p) (region-end)))
- (if (and (memq last-command '(org-shiftmetaright org-shiftmetaleft))
- (memq this-command '(org-shiftmetaright org-shiftmetaleft)))
- (setq beg org-last-indent-begin-marker
- end org-last-indent-end-marker)
- (org-beginning-of-item)
- (setq beg (move-marker org-last-indent-begin-marker (point)))
- (if no-subtree
- (org-end-of-item-text-before-children)
- (org-end-of-item))
- (setq end (move-marker org-last-indent-end-marker (or end (point)))))
- (goto-char beg)
- (setq ind-bul (org-item-indent-positions)
- ind (caar ind-bul)
- ind-down (car (nth 2 ind-bul))
- ind-up (car (nth 1 ind-bul))
- delta (if (> arg 0)
- (if ind-down (- ind-down ind) 2)
- (if ind-up (- ind-up ind) -2)))
- (if (and (< (+ delta ind) origin-ind)
- ;; verify we're not at the top level item
- (/= (point-at-bol) (org-list-top-point)))
- (error "Cannot outdent beyond top level item"))
- (while (< (point) end)
- (beginning-of-line 1)
- (skip-chars-forward " \t") (setq ind1 (current-column))
- (delete-region (point-at-bol) (point))
- (or (eolp) (org-indent-to-column (+ ind1 delta)))
- (beginning-of-line 2)))
- (org-fix-bullet-type
- (and (> arg 0)
- (not firstp)
- (cdr (assoc (cdr (nth 0 ind-bul)) org-list-demote-modify-bullet))))
- (org-maybe-renumber-ordered-list-safe)
- (save-excursion
- (beginning-of-line 0)
- (ignore-errors (org-beginning-of-item))
- (org-maybe-renumber-ordered-list-safe))))
- (defun org-item-indent-positions ()
- "Return indentation for plain list items.
- This returns a list with three values: The current indentation, the
- parent indentation and the indentation a child should have.
- Assumes cursor in item line."
- (let* ((bolpos (point-at-bol))
- (ind (org-get-indentation))
- (bullet (org-get-bullet))
- ind-down ind-up bullet-up bullet-down pos)
- (save-excursion
- (org-beginning-of-item-list)
- (skip-chars-backward "\n\r \t")
- (when (org-in-item-p)
- (org-beginning-of-item)
- (let ((prev-indent (org-get-indentation)))
- (when (< prev-indent ind)
- (setq ind-up prev-indent)
- (setq bullet-up (org-get-bullet))))))
- (setq pos (point))
- (save-excursion
- (cond
- ((and (ignore-errors (progn (org-previous-item) t))
- (or (end-of-line) t)
- (org-search-forward-unenclosed (org-item-re) bolpos t))
- (setq ind-down (org-get-indentation)
- bullet-down (org-get-bullet)))
- ((and (goto-char pos)
- (org-at-item-p))
- (goto-char (match-end 0))
- (skip-chars-forward " \t")
- (setq ind-down (current-column)
- bullet-down (org-get-bullet)))))
- (if (and bullet-down (string-match "\\`[0-9]+\\(\\.\\|)\\)\\'" bullet-down))
- (setq bullet-down (concat "1" (match-string 1 bullet-down))))
- (if (and bullet-up (string-match "\\`[0-9]+\\(\\.\\|)\\)\\'" bullet-up))
- (setq bullet-up (concat "1" (match-string 1 bullet-up))))
- (if (and bullet (string-match "\\`[0-9]+\\(\\.\\|)\\)\\'" bullet))
- (setq bullet (concat "1" (match-string 1 bullet))))
- (list (cons ind bullet)
- (cons ind-up bullet-up)
- (cons ind-down bullet-down))))
- (defvar org-tab-ind-state) ; defined in org.el
- (defun org-cycle-item-indentation ()
- (let ((org-suppress-item-indentation t)
- (org-adapt-indentation nil))
- (cond
- ((and (looking-at "[ \t]*$")
- (org-looking-back "^\\([ \t]*\\)\\([-+*]\\|[0-9]+[).]\\)[ \t]+"))
- (setq this-command 'org-cycle-item-indentation)
- (if (eq last-command 'org-cycle-item-indentation)
- (condition-case nil
- (progn (org-outdent-item 1)
- (if (equal org-tab-ind-state (org-get-indentation))
- (org-outdent-item 1))
- (end-of-line 1))
- (error
- (progn
- (while (< (org-get-indentation) org-tab-ind-state)
- (progn (org-indent-item 1) (end-of-line 1)))
- (setq this-command 'org-cycle))))
- (setq org-tab-ind-state (org-get-indentation))
- (org-indent-item 1))
- t))))
- ;;; Bullets
- (defun org-get-bullet ()
- (save-excursion
- (goto-char (point-at-bol))
- (and (looking-at
- "^\\([ \t]*\\([-+]\\|\\([0-9]+[.)]\\)\\)\\|[ \t]+\\(\\*\\)\\)\\( \\|$\\)")
- (or (match-string 2) (match-string 4)))))
- (defun org-fix-bullet-type (&optional force-bullet)
- "Make sure all items in this list have the same bullet as the first item.
- Also, fix the indentation."
- (interactive)
- (unless (org-at-item-p) (error "This is not a list"))
- (org-preserve-lc
- (let* ((bullet
- (progn
- (org-beginning-of-item-list)
- (looking-at "[ \t]*\\(\\S-+\\)")
- (concat (or force-bullet (match-string 1)) " "
- ;; Do we need to concat another white space ?
- (when (and org-list-two-spaces-after-bullet-regexp
- (string-match org-list-two-spaces-after-bullet-regexp bullet))
- " "))))
- (replace-bullet
- (lambda (result bullet)
- (let* ((old (progn
- (skip-chars-forward " \t")
- (looking-at "\\S-+ *")
- (match-string 0))))
- (unless (equal bullet old)
- (replace-match bullet)
- ;; When bullet lengths are differents, move the whole
- ;; sublist accordingly
- (org-shift-item-indentation (- (length bullet) (length old))))))))
- (org-apply-on-list replace-bullet nil bullet)
- ;; Fix item numbers if necessary
- (when (string-match "[0-9]" bullet) (org-renumber-ordered-list)))))
- (defun org-renumber-ordered-list (&optional arg)
- "Renumber an ordered plain list.
- Cursor needs to be in the first line of an item, the line that starts
- with something like \"1.\" or \"2)\". Start to count at ARG or 1."
- (interactive "p")
- (unless (and (org-at-item-p)
- (match-beginning 3))
- (error "This is not an ordered list"))
- (org-preserve-lc
- (let* ((offset (progn
- (org-beginning-of-item)
- (or (and (looking-at "[ \t]*\\[@start:\\([0-9]+\\)")
- (string-to-number (match-string 1)))
- arg
- 1)))
- (item-fmt (progn
- (looking-at "[ \t]*[0-9]+\\([.)]\\)")
- (concat "%d" (or (match-string 1) "."))))
- ;; Here is the function applied at each item of the list.
- (renumber-item (lambda (counter off fmt)
- (let* ((new (format fmt (+ counter off)))
- (old (progn
- (looking-at (org-item-re))
- (match-string 2)))
- (begin (match-beginning 2))
- (end (match-end 2)))
- (delete-region begin end)
- (goto-char begin)
- (insert new)
- ;; In case item number went from 9. to 10.
- ;; or the other way.
- (org-shift-item-indentation (- (length new) (length old)))
- (1+ counter)))))
- (org-apply-on-list renumber-item 0 offset item-fmt))))
- (defun org-maybe-renumber-ordered-list ()
- "Renumber the ordered list at point if setup allows it.
- This tests the user option `org-auto-renumber-ordered-lists' before
- doing the renumbering."
- (interactive)
- (when (and org-auto-renumber-ordered-lists
- (org-at-item-p))
- (if (match-beginning 3)
- (org-renumber-ordered-list 1)
- (org-fix-bullet-type))))
- (defun org-maybe-renumber-ordered-list-safe ()
- (ignore-errors
- (save-excursion
- (org-maybe-renumber-ordered-list))))
- (defun org-cycle-list-bullet (&optional which)
- "Cycle through the different itemize/enumerate bullets.
- This cycle the entire list level through the sequence:
- `-' -> `+' -> `*' -> `1.' -> `1)'
- If WHICH is a string, use that as the new bullet. If WHICH is an integer,
- 0 means `-', 1 means `+' etc."
- (interactive "P")
- (org-preserve-lc
- (let* ((current (progn
- (org-beginning-of-item-list)
- (org-at-item-p)
- (match-string 0)))
- (prevp (eq which 'previous))
- (new (cond
- ((and (numberp which)
- (nth (1- which) '("-" "+" "*" "1." "1)"))))
- ((string-match "-" current) (if prevp "1)" "+"))
- ((string-match "\\+" current)
- (if prevp "-" (if (looking-at "\\S-") "1." "*")))
- ((string-match "\\*" current) (if prevp "+" "1."))
- ((string-match "\\." current)
- (if prevp (if (looking-at "\\S-") "+" "*") "1)"))
- ((string-match ")" current) (if prevp "1." "-"))
- (t (error "This should not happen"))))
- (old (and (looking-at "\\([ \t]*\\)\\(\\S-+\\)")
- (match-string 2))))
- (replace-match (concat "\\1" new))
- (org-shift-item-indentation (- (length new) (length old)))
- (org-fix-bullet-type)
- (org-maybe-renumber-ordered-list))))
- ;;; Checkboxes
- (defun org-toggle-checkbox (&optional toggle-presence)
- "Toggle the checkbox in the current line.
- With prefix arg TOGGLE-PRESENCE, add or remove checkboxes.
- With double prefix, set checkbox to [-].
- When there is an active region, toggle status or presence of the checkbox
- in the first line, and make every item in the region have the same
- status or presence, respectively.
- If the cursor is in a headline, apply this to all checkbox items in the
- text below the heading."
- (interactive "P")
- (catch 'exit
- (let (beg end status first-present first-status blocked)
- (cond
- ((org-region-active-p)
- (setq beg (region-beginning) end (region-end)))
- ((org-on-heading-p)
- (setq beg (point) end (save-excursion (outline-next-heading) (point))))
- ((org-at-item-checkbox-p)
- (save-excursion
- (if (equal toggle-presence '(4))
- (progn
- (replace-match "")
- (goto-char (match-beginning 0))
- (just-one-space))
- (when (setq blocked (org-checkbox-blocked-p))
- (error "Checkbox blocked because of unchecked box in line %d"
- blocked))
- (replace-match
- (cond ((equal toggle-presence '(16)) "[-]")
- ((member (match-string 0) '("[ ]" "[-]")) "[X]")
- (t "[ ]"))
- t t)))
- (throw 'exit t))
- ((org-at-item-p)
- ;; add a checkbox
- (save-excursion
- (goto-char (match-end 0))
- (insert "[ ] "))
- (throw 'exit t))
- (t (error "Not at a checkbox or heading, and no active region")))
- (setq end (move-marker (make-marker) end))
- (save-excursion
- (goto-char beg)
- (setq first-present (org-at-item-checkbox-p)
- first-status
- (save-excursion
- (and (org-search-forward-unenclosed "[ \t]\\(\\[[ X]\\]\\)" end t)
- (equal (match-string 1) "[X]"))))
- (while (< (point) end)
- (if toggle-presence
- (cond
- ((and first-present (org-at-item-checkbox-p))
- (save-excursion
- (replace-match "")
- (goto-char (match-beginning 0))
- (just-one-space)))
- ((and (not first-present) (not (org-at-item-checkbox-p))
- (org-at-item-p))
- (save-excursion
- (goto-char (match-end 0))
- (insert "[ ] "))))
- (when (org-at-item-checkbox-p)
- (setq status (equal (match-string 0) "[X]"))
- (replace-match
- (if first-status "[ ]" "[X]") t t)))
- (beginning-of-line 2)))))
- (org-update-checkbox-count-maybe))
- (defun org-reset-checkbox-state-subtree ()
- "Reset all checkboxes in an entry subtree."
- (interactive "*")
- (save-restriction
- (save-excursion
- (org-narrow-to-subtree)
- (org-show-subtree)
- (goto-char (point-min))
- (let ((end (point-max)))
- (while (< (point) end)
- (when (org-at-item-checkbox-p)
- (replace-match "[ ]" t t))
- (beginning-of-line 2))))
- (org-update-checkbox-count-maybe)))
- (defvar org-checkbox-statistics-hook nil
- "Hook that is run whenever Org thinks checkbox statistics should be updated.
- This hook runs even if `org-provide-checkbox-statistics' is nil, to it can
- be used to implement alternative ways of collecting statistics information.")
- (defun org-update-checkbox-count-maybe ()
- "Update checkbox statistics unless turned off by user."
- (when org-provide-checkbox-statistics
- (org-update-checkbox-count))
- (run-hooks 'org-checkbox-statistics-hook))
- (defun org-update-checkbox-count (&optional all)
- "Update the checkbox statistics in the current section.
- This will find all statistic cookies like [57%] and [6/12] and update them
- with the current numbers. With optional prefix argument ALL, do this for
- the whole buffer."
- (interactive "P")
- (save-excursion
- (let* ((buffer-invisibility-spec (org-inhibit-invisibility)) ; Emacs 21
- (beg (condition-case nil
- (progn (org-back-to-heading) (point))
- (error (point-min))))
- (end (move-marker (make-marker)
- (progn (outline-next-heading) (point))))
- (re "\\(\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*/[0-9]*\\]\\)\\)")
- (re-box "^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +\\(\\[[- X]\\]\\)")
- (re-find (concat re "\\|" re-box))
- beg-cookie end-cookie is-percent c-on c-off lim new
- eline curr-ind next-ind continue-from startsearch
- (recursive
- (or (not org-hierarchical-checkbox-statistics)
- (string-match "\\<recursive\\>"
- (or (ignore-errors
- (org-entry-get nil "COOKIE_DATA"))
- ""))))
- (cstat 0)
- )
- (when all
- (goto-char (point-min))
- (outline-next-heading)
- (setq beg (point) end (point-max)))
- (goto-char end)
- ;; find each statistics cookie
- (while (and (org-search-backward-unenclosed re-find beg t)
- (not (save-match-data
- (and (org-on-heading-p)
- (string-match "\\<todo\\>"
- (downcase
- (or (org-entry-get
- nil "COOKIE_DATA")
- "")))))))
- (setq beg-cookie (match-beginning 1)
- end-cookie (match-end 1)
- cstat (+ cstat (if end-cookie 1 0))
- startsearch (point-at-eol)
- continue-from (match-beginning 0)
- is-percent (match-beginning 2)
- lim (cond
- ((org-on-heading-p) (outline-next-heading) (point))
- ((org-at-item-p) (org-end-of-item) (point))
- (t nil))
- c-on 0
- c-off 0)
- (when lim
- ;; find first checkbox for this cookie and gather
- ;; statistics from all that are at this indentation level
- (goto-char startsearch)
- (if (org-search-forward-unenclosed re-box lim t)
- (progn
- (org-beginning-of-item)
- (setq curr-ind (org-get-indentation))
- (setq next-ind curr-ind)
- (while (and (bolp) (org-at-item-p)
- (if recursive
- (<= curr-ind next-ind)
- (= curr-ind next-ind)))
- (save-excursion (end-of-line) (setq eline (point)))
- (if (org-search-forward-unenclosed re-box eline t)
- (if (member (match-string 2) '("[ ]" "[-]"))
- (setq c-off (1+ c-off))
- (setq c-on (1+ c-on))))
- (if (not recursive)
- ;; org-get-next-item goes through list-enders
- ;; with proper limit.
- (goto-char (or (org-get-next-item (point) lim) lim))
- (end-of-line)
- (when (org-search-forward-unenclosed (org-item-re) lim t)
- (beginning-of-line)))
- (setq next-ind (org-get-indentation)))))
- (goto-char continue-from)
- ;; update cookie
- (when end-cookie
- (setq new (if is-percent
- (format "[%d%%]" (/ (* 100 c-on) (max 1 (+ c-on c-off))))
- (format "[%d/%d]" c-on (+ c-on c-off))))
- (goto-char beg-cookie)
- (insert new)
- (delete-region (point) (+ (point) (- end-cookie beg-cookie))))
- ;; update items checkbox if it has one
- (when (org-at-item-p)
- (org-beginning-of-item)
- (when (and (> (+ c-on c-off) 0)
- (org-search-forward-unenclosed re-box (point-at-eol) t))
- (setq beg-cookie (match-beginning 2)
- end-cookie (match-end 2))
- (delete-region beg-cookie end-cookie)
- (goto-char beg-cookie)
- (cond ((= c-off 0) (insert "[X]"))
- ((= c-on 0) (insert "[ ]"))
- (t (insert "[-]")))
- )))
- (goto-char continue-from))
- (when (interactive-p)
- (message "Checkbox statistics updated %s (%d places)"
- (if all "in entire file" "in current outline entry") cstat)))))
- (defun org-get-checkbox-statistics-face ()
- "Select the face for checkbox statistics.
- The face will be `org-done' when all relevant boxes are checked.
- Otherwise it will be `org-todo'."
- (if (match-end 1)
- (if (equal (match-string 1) "100%")
- 'org-checkbox-statistics-done
- 'org-checkbox-statistics-todo)
- (if (and (> (match-end 2) (match-beginning 2))
- (equal (match-string 2) (match-string 3)))
- 'org-checkbox-statistics-done
- 'org-checkbox-statistics-todo)))
- ;;; Misc Tools
- (defun org-apply-on-list (function init-value &rest args)
- "Call FUNCTION for each item of a the list under point.
- FUNCTION must be called with at least one argument : a return
- value that will contain the value returned by the function at
- the previous item, plus ARGS extra arguments. INIT-VALUE will be
- the value passed to the function at the first item of the list.
- As an example, (org-apply-on-list (lambda (result) (1+ result)) 0)
- will return the number of items in the current list.
- Sublists of the list are skipped. Cursor is always at the
- beginning of the item."
- (save-excursion
- (let ((move-down-action
- (lambda (pos value &rest args)
- (goto-char pos)
- (let ((return-value (apply function value args))
- ;; we need to recompute each time end of list in case
- ;; function modified list.
- (next-p (org-get-next-item pos (org-end-of-item-list))))
- (if next-p
- (apply move-down-action next-p return-value args)
- return-value)))))
- (apply move-down-action (org-beginning-of-item-list) init-value args))))
- (defun org-sort-list (&optional with-case sorting-type getkey-func compare-func)
- "Sort plain list items.
- The cursor may be at any item of the list that should be sorted.
- Sublists are not sorted. Checkboxes, if any, are ignored.
- Sorting can be alphabetically, numerically, by date/time as given by
- a time stamp, by a property or by priority.
- The command prompts for the sorting type unless it has been given
- to the function through the SORTING-TYPE argument, which needs to
- be a character, \(?n ?N ?a ?A ?t ?T ?f ?F). Here is the precise
- meaning of each character:
- n Numerically, by converting the beginning of the item to a number.
- a Alphabetically.
- t By date/time, either the first active time stamp in the entry, if
- any, or by the first inactive one. In a timer list, sorts the timers.
- Only the first line of item is checked.
- Capital letters will reverse the sort order.
- If the SORTING-TYPE is ?f or ?F, then GETKEY-FUNC specifies a
- function to be called with point at the beginning of the record.
- It must return either a string or a number that should serve as
- the sorting key for that record.
- Comparing entries ignores case by default. However, with an
- optional argument WITH-CASE, the sorting considers case as well."
- (interactive "P")
- (let* ((case-func (if with-case 'identity 'downcase))
- (start (org-beginning-of-item-list))
- (end (save-excursion (org-end-of-item-list)))
- (sorting-type
- (progn
- (message
- "Sort plain list: [a]lpha [n]umeric [t]ime [f]unc A/N/T/F means reversed:")
- (read-char-exclusive)))
- (getkey-func (and (= (downcase sorting-type) ?f)
- (org-icompleting-read "Sort using function: "
- obarray 'fboundp t nil nil)
- (intern getkey-func))))
- (message "Sorting items...")
- (save-restriction
- (narrow-to-region start end)
- (let* ((dcst (downcase sorting-type))
- (case-fold-search nil)
- (now (current-time))
- (sort-func (cond
- ((= dcst ?a) 'string<)
- ((= dcst ?f) compare-func)
- ((= dcst ?t) '<)
- (t nil)))
- (begin-record (lambda ()
- (skip-chars-forward " \r\t\n")
- (beginning-of-line)))
- (end-record (lambda ()
- (goto-char (org-end-of-item-before-blank))))
- (value-to-sort
- (lambda ()
- (when (looking-at "[ \t]*[-+*0-9.)]+\\([ \t]+\\[[- X]\\]\\)?[ \t]+")
- (cond
- ((= dcst ?n)
- (string-to-number (buffer-substring (match-end 0)
- (point-at-eol))))
- ((= dcst ?a)
- (buffer-substring (match-end 0) (point-at-eol)))
- ((= dcst ?t)
- (cond
- ;; If it is a timer list, convert timer to seconds
- ((org-at-item-timer-p)
- (org-timer-hms-to-secs (match-string 1)))
- ((or (org-search-forward-unenclosed org-ts-regexp
- (point-at-eol) t)
- (org-search-forward-unenclosed org-ts-regexp-both
- (point-at-eol) t))
- (org-time-string-to-seconds (match-string 0)))
- (t (org-float-time now))))
- ((= dcst ?f)
- (if getkey-func
- (let ((value (funcall getkey-func)))
- (if (stringp value)
- (funcall case-func value)
- value))
- (error "Invalid key function `%s'" getkey-func)))
- (t (error "Invalid sorting type `%c'" sorting-type)))))))
- (sort-subr (/= dcst sorting-type) begin-record end-record value-to-sort nil sort-func)
- (org-maybe-renumber-ordered-list)
- (run-hooks 'org-after-sorting-entries-or-items-hook)
- (message "Sorting items...done")))))
- ;;; Send and receive lists
- (defun org-list-parse-list (&optional delete)
- "Parse the list at point and maybe DELETE it.
- Return a list containing first level items as strings and
- sublevels as a list of strings."
- (let* ((start (goto-char (org-list-top-point)))
- (end (org-list-bottom-point))
- output itemsep ltype)
- (while (org-search-forward-unenclosed (org-item-re) end t)
- (save-excursion
- (beginning-of-line)
- (cond ((looking-at-p "^[ \t]*[0-9]")
- (setq itemsep "[0-9]+\\(?:\\.\\|)\\)"
- ltype 'ordered))
- ((looking-at-p "^.*::")
- (setq itemsep "[-+*]" ltype 'descriptive))
- (t (setq itemsep "[-+*]" ltype 'unordered))))
- (let* ((indent1 (org-get-indentation))
- (nextitem (or (org-get-next-item (point) end) end))
- (item (org-trim (buffer-substring (point) (org-end-of-item-text-before-children))))
- (nextindent (if (= (point) end) 0 (org-get-indentation)))
- (item (if (string-match "^\\[\\([xX ]\\)\\]" item)
- (replace-match (if (equal (match-string 1 item) " ")
- "[CBOFF]"
- "[CBON]")
- t nil item)
- item)))
- (push item output)
- (when (> nextindent indent1)
- (save-restriction
- (narrow-to-region (point) nextitem)
- (push (org-list-parse-list) output)))))
- (when delete
- (delete-region start end)
- (save-match-data
- (when (looking-at (org-list-end-re))
- (replace-match "\n"))))
- (setq output (nreverse output))
- (push ltype output)))
- (defun org-list-make-subtree ()
- "Convert the plain list at point into a subtree."
- (interactive)
- (goto-char (org-list-top-point))
- (let ((list (org-list-parse-list t)) nstars)
- (save-excursion
- (if (ignore-errors
- (org-back-to-heading))
- (progn (org-search-forward-unenclosed org-complex-heading-regexp nil t)
- (setq nstars (length (match-string 1))))
- (setq nstars 0)))
- (org-list-make-subtrees list (1+ nstars))))
- (defun org-list-make-subtrees (list level)
- "Convert LIST into subtrees starting at LEVEL."
- (if (symbolp (car list))
- (org-list-make-subtrees (cdr list) level)
- (mapcar (lambda (item)
- (if (stringp item)
- (insert (make-string
- (if org-odd-levels-only
- (1- (* 2 level)) level) ?*) " " item "\n")
- (org-list-make-subtrees item (1+ level))))
- list)))
- (defun org-list-insert-radio-list ()
- "Insert a radio list template appropriate for this major mode."
- (interactive)
- (let* ((e (assq major-mode org-list-radio-list-templates))
- (txt (nth 1 e))
- name pos)
- (unless e (error "No radio list setup defined for %s" major-mode))
- (setq name (read-string "List name: "))
- (while (string-match "%n" txt)
- (setq txt (replace-match name t t txt)))
- (or (bolp) (insert "\n"))
- (setq pos (point))
- (insert txt)
- (goto-char pos)))
- (defun org-list-send-list (&optional maybe)
- "Send a transformed version of this list to the receiver position.
- With argument MAYBE, fail quietly if no transformation is defined for
- this list."
- (interactive)
- (catch 'exit
- (unless (org-at-item-p) (error "Not at a list"))
- (save-excursion
- (re-search-backward "#\\+ORGLST" nil t)
- (unless (looking-at "[ \t]*#\\+ORGLST[: \t][ \t]*SEND[ \t]+\\([^ \t\r\n]+\\)[ \t]+\\([^ \t\r\n]+\\)\\([ \t]+.*\\)?")
- (if maybe
- (throw 'exit nil)
- (error "Don't know how to transform this list"))))
- (let* ((name (match-string 1))
- (transform (intern (match-string 2)))
- (bottom-point
- (save-excursion
- (re-search-forward "\\(\\\\end{comment}\\|@end ignore\\|-->\\)" nil t)
- (match-beginning 0)))
- (top-point
- (progn
- (re-search-backward "#\\+ORGLST" nil t)
- (re-search-forward (org-item-re) bottom-point t)
- (match-beginning 0)))
- (list (save-restriction
- (narrow-to-region top-point bottom-point)
- (org-list-parse-list)))
- beg txt)
- (unless (fboundp transform)
- (error "No such transformation function %s" transform))
- (let ((txt (funcall transform list)))
- ;; Find the insertion place
- (save-excursion
- (goto-char (point-min))
- (unless (re-search-forward
- (concat "BEGIN RECEIVE ORGLST +" name "\\([ \t]\\|$\\)") nil t)
- (error "Don't know where to insert translated list"))
- (goto-char (match-beginning 0))
- (beginning-of-line 2)
- (setq beg (point))
- (unless (re-search-forward (concat "END RECEIVE ORGLST +" name) nil t)
- (error "Cannot find end of insertion region"))
- (beginning-of-line 1)
- (delete-region beg (point))
- (goto-char beg)
- (insert txt "\n")))
- (message "List converted and installed at receiver location"))))
- (defun org-list-to-generic (list params)
- "Convert a LIST parsed through `org-list-parse-list' to other formats.
- Valid parameters PARAMS are
- :ustart String to start an unordered list
- :uend String to end an unordered list
- :ostart String to start an ordered list
- :oend String to end an ordered list
- :dstart String to start a descriptive list
- :dend String to end a descriptive list
- :dtstart String to start a descriptive term
- :dtend String to end a descriptive term
- :ddstart String to start a description
- :ddend String to end a description
- :splice When set to t, return only list body lines, don't wrap
- them into :[u/o]start and :[u/o]end. Default is nil.
- :istart String to start a list item
- :iend String to end a list item
- :isep String to separate items
- :lsep String to separate sublists
- :cboff String to insert for an unchecked checkbox
- :cbon String to insert for a checked checkbox"
- (interactive)
- (let* ((p params) sublist
- (splicep (plist-get p :splice))
- (ostart (plist-get p :ostart))
- (oend (plist-get p :oend))
- (ustart (plist-get p :ustart))
- (uend (plist-get p :uend))
- (dstart (plist-get p :dstart))
- (dend (plist-get p :dend))
- (dtstart (plist-get p :dtstart))
- (dtend (plist-get p :dtend))
- (ddstart (plist-get p :ddstart))
- (ddend (plist-get p :ddend))
- (istart (plist-get p :istart))
- (iend (plist-get p :iend))
- (isep (plist-get p :isep))
- (lsep (plist-get p :lsep))
- (cbon (plist-get p :cbon))
- (cboff (plist-get p :cboff)))
- (let ((wrapper
- (cond ((eq (car list) 'ordered)
- (concat ostart "\n%s" oend "\n"))
- ((eq (car list) 'unordered)
- (concat ustart "\n%s" uend "\n"))
- ((eq (car list) 'descriptive)
- (concat dstart "\n%s" dend "\n"))))
- rtn term defstart defend)
- (while (setq sublist (pop list))
- (cond ((symbolp sublist) nil)
- ((stringp sublist)
- (when (string-match "^\\(.*\\) ::" sublist)
- (setq term (org-trim (format (concat dtstart "%s" dtend)
- (match-string 1 sublist))))
- (setq sublist (concat ddstart
- (org-trim (substring sublist (match-end 0)))
- ddend)))
- (if (string-match "\\[CBON\\]" sublist)
- (setq sublist (replace-match cbon t t sublist)))
- (if (string-match "\\[CBOFF\\]" sublist)
- (setq sublist (replace-match cboff t t sublist)))
- (if (string-match "\\[-\\]" sublist)
- (setq sublist (replace-match "$\\boxminus$" t t sublist)))
- (setq rtn (concat rtn istart term sublist iend isep)))
- (t (setq rtn (concat rtn ;; previous list
- lsep ;; list separator
- (org-list-to-generic sublist p)
- lsep ;; list separator
- )))))
- (format wrapper rtn))))
- (defun org-list-to-latex (list &optional params)
- "Convert LIST into a LaTeX list.
- LIST is as returned by `org-list-parse-list'. PARAMS is a property list
- with overruling parameters for `org-list-to-generic'."
- (org-list-to-generic
- list
- (org-combine-plists
- '(:splicep nil :ostart "\\begin{enumerate}" :oend "\\end{enumerate}"
- :ustart "\\begin{itemize}" :uend "\\end{itemize}"
- :dstart "\\begin{description}" :dend "\\end{description}"
- :dtstart "[" :dtend "]"
- :ddstart "" :ddend ""
- :istart "\\item " :iend ""
- :isep "\n" :lsep "\n"
- :cbon "\\texttt{[X]}" :cboff "\\texttt{[ ]}")
- params)))
- (defun org-list-to-html (list &optional params)
- "Convert LIST into a HTML list.
- LIST is as returned by `org-list-parse-list'. PARAMS is a property list
- with overruling parameters for `org-list-to-generic'."
- (org-list-to-generic
- list
- (org-combine-plists
- '(:splicep nil :ostart "<ol>" :oend "</ol>"
- :ustart "<ul>" :uend "</ul>"
- :dstart "<dl>" :dend "</dl>"
- :dtstart "<dt>" :dtend "</dt>"
- :ddstart "<dd>" :ddend "</dd>"
- :istart "<li>" :iend "</li>"
- :isep "\n" :lsep "\n"
- :cbon "<code>[X]</code>" :cboff "<code>[ ]</code>")
- params)))
- (defun org-list-to-texinfo (list &optional params)
- "Convert LIST into a Texinfo list.
- LIST is as returned by `org-list-parse-list'. PARAMS is a property list
- with overruling parameters for `org-list-to-generic'."
- (org-list-to-generic
- list
- (org-combine-plists
- '(:splicep nil :ostart "@itemize @minus" :oend "@end itemize"
- :ustart "@enumerate" :uend "@end enumerate"
- :dstart "@table" :dend "@end table"
- :dtstart "@item " :dtend "\n"
- :ddstart "" :ddend ""
- :istart "@item\n" :iend ""
- :isep "\n" :lsep "\n"
- :cbon "@code{[X]}" :cboff "@code{[ ]}")
- params)))
- (provide 'org-list)
- ;; arch-tag: 73cf50c1-200f-4d1d-8a53-4e842a5b11c8
- ;;; org-list.el ends here
|