1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004 |
- ;;; 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.4
- ;;
- ;; 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.
- ;; The fundamental idea behind lists work is to use structures. A
- ;; structure is a snapshot of the list, in the shape of data tree (see
- ;; `org-list-struct').
- ;; Once the list structure is stored, it is possible to make changes
- ;; directly on it or get useful information about the list, with the
- ;; two helper functions, namely `org-list-parents-alist' and
- ;; `org-list-prevs-alist', and using accessors or methods.
- ;; Structure is eventually applied to the buffer with
- ;; `org-list-write-struct'. This function repairs (bullets,
- ;; indentation, checkboxes) the structure before applying it. It
- ;; should be called near the end of any function working on
- ;; structures.
- ;; Thus, a function applying to lists should usually follow this
- ;; template:
- ;; 1. Verify point is in a list and grab item beginning (with the same
- ;; function `org-in-item-p'). If the function requires the cursor
- ;; to be at item's bullet, `org-at-item-p' is more selective. If
- ;; the cursor is amidst the buffer, it is possible to find the
- ;; closest item with `org-list-search-backward', or
- ;; `org-list-search-forward', applied to `org-item-beginning-re'.
- ;; 2. Get list structure with `org-list-struct'.
- ;; 3. Compute one, or both, helper functions,
- ;; (`org-list-parents-alist', `org-list-prevs-alist') depending on
- ;; needed accessors.
- ;; 4. Proceed with the modifications, using methods and accessors.
- ;; 5. Verify and apply structure to buffer, using
- ;; `org-list-write-struct'. Possibly use
- ;; `org-update-checkbox-count-maybe' if checkboxes might have been
- ;; modified.
- ;; Computing a list structure can be a costly operation on huge lists
- ;; (a few thousand lines long). Thus, code should follow the rule :
- ;; "collect once, use many". As a corollary, it is usally a bad idea
- ;; to use directly an interactive function inside the code, as those,
- ;; being independant entities, read the whole list structure another
- ;; time.
- ;;; Code:
- (eval-when-compile
- (require 'cl))
- (require 'org-macs)
- (require 'org-compat)
- (defvar org-blank-before-new-entry)
- (defvar org-complex-heading-regexp)
- (defvar org-description-max-indent)
- (defvar org-drawer-regexp)
- (defvar org-drawers)
- (defvar org-M-RET-may-split-line)
- (defvar org-odd-levels-only)
- (defvar org-outline-regexp)
- (defvar org-ts-regexp)
- (defvar org-ts-regexp-both)
- (declare-function org-at-heading-p "org" (&optional ignored))
- (declare-function org-back-over-empty-lines "org" ())
- (declare-function org-back-to-heading "org" (&optional invisible-ok))
- (declare-function org-combine-plists "org" (&rest plists))
- (declare-function org-count "org" (cl-item cl-seq))
- (declare-function org-current-level "org" ())
- (declare-function org-entry-get "org"
- (pom property &optional inherit literal-nil))
- (declare-function org-get-indentation "org" (&optional line))
- (declare-function org-icompleting-read "org" (&rest args))
- (declare-function org-in-regexp "org" (re &optional nlines visually))
- (declare-function org-in-regexps-block-p "org"
- (start-re end-re &optional bound))
- (declare-function org-inlinetask-goto-beginning "org-inlinetask" ())
- (declare-function org-inlinetask-goto-end "org-inlinetask" ())
- (declare-function org-inlinetask-in-task-p "org-inlinetask" ())
- (declare-function org-inlinetask-outline-regexp "org-inlinetask" ())
- (declare-function org-invisible-p "org" ())
- (declare-function org-level-increment "org" ())
- (declare-function org-narrow-to-subtree "org" ())
- (declare-function org-on-heading-p "org" (&optional invisible-ok))
- (declare-function org-previous-line-empty-p "org" ())
- (declare-function org-remove-if "org" (predicate seq))
- (declare-function org-show-subtree "org" ())
- (declare-function org-time-string-to-seconds "org" (s))
- (declare-function org-timer-hms-to-secs "org-timer" (hms))
- (declare-function org-timer-item "org-timer" (&optional arg))
- (declare-function org-trim "org" (s))
- (declare-function org-uniquify "org" (list))
- (declare-function outline-next-heading "outline" ())
- (declare-function outline-previous-heading "outline" ())
- ;;; Configuration variables
- (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-alphabetical-lists nil
- "Non-nil means single character alphabetical bullets are allowed.
- Both uppercase and lowercase are handled. Lists with more than 26
- items will fallback to standard numbering. Alphabetical counters
- like \"[@c]\" will be recognized."
- :group 'org-plain-lists
- :type 'boolean)
- (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-list-ending-method 'both
- "Determine where plain lists should end.
- Valid values are: `regexp', `indent' or `both'.
- When set to `regexp', Org will look into two variables,
- `org-empty-line-terminates-plain-lists' and the more general
- `org-list-end-regexp', to determine what will end lists.
- When set to `indent', a list will end whenever a line following
- an item, but not starting one, is less or equally indented than
- the first item of the list.
- When set to `both', each of the preceding methods is applied to
- determine lists endings. This is the default method."
- :group 'org-plain-lists
- :type '(choice
- (const :tag "With a regexp defining ending" regexp)
- (const :tag "With indentation of regular (no bullet) text" indent)
- (const :tag "With both methods" both)))
- (defcustom org-empty-line-terminates-plain-lists nil
- "Non-nil means an empty line ends all plain list levels.
- This variable only makes sense if `org-list-ending-method' is set
- to `regexp' or `both'. This is then equivalent to set
- `org-list-end-regexp' to \"^[ \\t]*$\"."
- :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-list-automatic-rules '((bullet . t)
- (checkbox . t)
- (indent . t))
- "Non-nil means apply set of rules when acting on lists.
- By default, automatic actions are taken when using
- \\[org-meta-return], \\[org-metaright], \\[org-metaleft],
- \\[org-shiftmetaright], \\[org-shiftmetaleft],
- \\[org-ctrl-c-minus], \\[org-toggle-checkbox] or
- \\[org-insert-todo-heading]. You can disable individually these
- rules by setting them to nil. Valid rules are:
- bullet when non-nil, cycling bullet do not allow lists at
- column 0 to have * as a bullet and descriptions lists
- to be numbered.
- checkbox when non-nil, checkbox statistics is updated each time
- you either insert a new checkbox or toggle a checkbox.
- It also prevents from inserting a checkbox in a
- description item.
- indent when non-nil, indenting or outdenting list top-item
- with its subtree will move the whole list and
- outdenting a list whose bullet is * to column 0 will
- change that bullet to \"-\"."
- :group 'org-plain-lists
- :type '(alist :tag "Sets of rules"
- :key-type
- (choice
- (const :tag "Bullet" bullet)
- (const :tag "Checkbox" checkbox)
- (const :tag "Indent" indent))
- :value-type
- (boolean :tag "Activate" :value t)))
- (defvar org-checkbox-statistics-hook nil
- "Hook that is run whenever Org thinks checkbox statistics should be updated.
- This hook runs even if checkbox rule in
- `org-list-automatic-rules' does not apply, so it can be used to
- implement alternative ways of collecting statistics
- information.")
- (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"))))
- (defvar org-list-forbidden-blocks '("example" "verse" "src")
- "Names of blocks where lists are not allowed.
- Names must be in lower case.")
- (defvar org-list-export-context '(block inlinetask)
- "Context types where lists will be interpreted during export.
- Valid types are `drawer', `inlinetask' and `block'. More
- specifically, type `block' is determined by the variable
- `org-list-forbidden-blocks'.")
- ;;; Predicates and regexps
- (defconst org-list-end-re (if org-empty-line-terminates-plain-lists
- "^[ \t]*\n"
- org-list-end-regexp)
- "Regex corresponding to the end of a list.
- It depends on `org-empty-line-terminates-plain-lists'.")
- (defconst org-list-full-item-re
- (concat "^[ \t]*\\(\\(?:[-+*]\\|\\(?:[0-9]+\\|[A-Za-z]\\)[.)]\\)[ \t]+\\)"
- "\\(?:\\[@\\(?:start:\\)?\\([0-9]+\\|[A-Za-z]\\)\\]\\)?"
- "\\(?:\\(\\[[ X-]\\]\\)[ \t]+\\)?"
- "\\(?:\\(.*\\)[ \t]+::[ \t]+\\)?")
- "Matches a list item and puts everything into groups:
- group 1: bullet
- group 2: counter
- group 3: checkbox
- group 4: description tag")
- (defun org-item-re ()
- "Return the correct regular expression for plain lists."
- (let ((term (cond
- ((eq org-plain-list-ordered-item-terminator t) "[.)]")
- ((= org-plain-list-ordered-item-terminator ?\)) ")")
- ((= org-plain-list-ordered-item-terminator ?.) "\\.")
- (t "[.)]")))
- (alpha (if org-alphabetical-lists "\\|[A-Za-z]" "")))
- (concat "\\([ \t]*\\([-+]\\|\\(\\([0-9]+" alpha "\\)" term
- "\\)\\)\\|[ \t]+\\*\\)\\([ \t]+\\|$\\)")))
- (defsubst org-item-beginning-re ()
- "Regexp matching the beginning of a plain list item."
- (concat "^" (org-item-re)))
- (defun org-list-at-regexp-after-bullet-p (regexp)
- "Is point at a list item with REGEXP after bullet?"
- (and (org-at-item-p)
- (save-excursion
- (goto-char (match-end 0))
- ;; Ignore counter if any
- (when (looking-at "\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?")
- (goto-char (match-end 0)))
- (looking-at regexp))))
- (defun org-in-item-p ()
- "Return item beginning position when in a plain list, nil otherwise.
- This checks `org-list-ending-method'."
- (save-excursion
- (beginning-of-line)
- (let* ((case-fold-search t)
- (context (org-list-context))
- (lim-up (car context))
- (inlinetask-re (and (featurep 'org-inlinetask)
- (org-inlinetask-outline-regexp)))
- (item-re (org-item-re))
- ;; Indentation isn't meaningful when point starts at an empty
- ;; line or an inline task.
- (ind-ref (if (or (looking-at "^[ \t]*$")
- (and inlinetask-re (looking-at inlinetask-re)))
- 10000
- (org-get-indentation))))
- (cond
- ((eq (nth 2 context) 'invalid) nil)
- ((looking-at item-re) (point))
- (t
- ;; Detect if cursor in amidst `org-list-end-re'. First, count
- ;; number HL of hard lines it takes, then call `org-in-regexp'
- ;; to compute its boundaries END-BOUNDS. When point is
- ;; in-between, move cursor before regexp beginning.
- (let ((hl 0) (i -1) end-bounds)
- (when (and (not (eq org-list-ending-method 'indent))
- (progn
- (while (setq i (string-match
- "[\r\n]" org-list-end-re (1+ i)))
- (setq hl (1+ hl)))
- (setq end-bounds (org-in-regexp org-list-end-re hl)))
- (>= (point) (car end-bounds))
- (< (point) (cdr end-bounds)))
- (goto-char (car end-bounds))
- (forward-line -1)))
- ;; Look for an item, less indented that reference line if
- ;; `org-list-ending-method' isn't `regexp'.
- (catch 'exit
- (while t
- (let ((ind (org-get-indentation)))
- (cond
- ;; This is exactly what we want.
- ((and (looking-at item-re)
- (or (< ind ind-ref)
- (eq org-list-ending-method 'regexp)))
- (throw 'exit (point)))
- ;; At upper bound of search or looking at the end of a
- ;; previous list: search is over.
- ((<= (point) lim-up) (throw 'exit nil))
- ((and (not (eq org-list-ending-method 'indent))
- (looking-at org-list-end-re))
- (throw 'exit nil))
- ;; Skip blocks, drawers, inline-tasks, blank lines
- ((looking-at "^[ \t]*#\\+end_")
- (re-search-backward "^[ \t]*#\\+begin_" nil t))
- ((looking-at "^[ \t]*:END:")
- (re-search-backward org-drawer-regexp nil t)
- (beginning-of-line))
- ((and inlinetask-re (looking-at inlinetask-re))
- (org-inlinetask-goto-beginning)
- (forward-line -1))
- ((looking-at "^[ \t]*$") (forward-line -1))
- ;; Text at column 0 cannot belong to a list: stop.
- ((zerop ind) (throw 'exit nil))
- ;; Normal text less indented than reference line, take
- ;; it as new reference.
- ((< ind ind-ref)
- (setq ind-ref ind)
- (forward-line -1))
- (t (forward-line -1)))))))))))
- (defun org-at-item-p ()
- "Is point in a line starting a hand-formatted item?"
- (save-excursion
- (beginning-of-line)
- (and (not (eq (nth 2 (org-list-context)) 'invalid))
- (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?"
- (org-list-at-regexp-after-bullet-p
- "\\([0-9]+:[0-9]+:[0-9]+\\)[ \t]+::[ \t]+"))
- (defun org-at-item-description-p ()
- "Is point at a description list item?"
- (org-list-at-regexp-after-bullet-p "\\(\\S-.+\\)[ \t]+::[ \t]+"))
- (defun org-at-item-checkbox-p ()
- "Is point at a line starting a plain-list item with a checklet?"
- (org-list-at-regexp-after-bullet-p "\\(\\[[- X]\\]\\)[ \t]+"))
- (defun org-at-item-counter-p ()
- "Is point at a line starting a plain-list item with a counter?"
- (and (org-at-item-p)
- (looking-at org-list-full-item-re)
- (match-string 2)))
- ;;; Structures and helper functions
- (defun org-list-context ()
- "Determine context, and its boundaries, around point.
- Context will be a cell like (MIN MAX CONTEXT) where MIN and MAX
- are boundaries and CONTEXT is a symbol among `drawer', `block',
- `invalid', `inlinetask' and nil.
- Contexts `block' and `invalid' refer to
- `org-list-forbidden-blocks'."
- (save-match-data
- (save-excursion
- (beginning-of-line)
- (let* ((outline-regexp (org-get-limited-outline-regexp))
- ;; Can't use org-drawers-regexp as this function might be
- ;; called in buffers not in Org mode
- (drawers-re (concat "^[ \t]*:\\("
- (mapconcat 'regexp-quote org-drawers "\\|")
- "\\):[ \t]*$"))
- (case-fold-search t)
- ;; Compute position of surrounding headings. This is the
- ;; default context.
- (heading
- (save-excursion
- (list
- (or (and (org-at-heading-p) (point-at-bol))
- (outline-previous-heading)
- (point-min))
- (or (outline-next-heading)
- (point-max))
- nil)))
- (prev-head (car heading))
- (next-head (nth 1 heading))
- ;; Is point inside a drawer?
- (drawerp
- (when (and (org-in-regexps-block-p
- drawers-re "^[ \t]*:END:" prev-head)
- (save-excursion
- (beginning-of-line)
- (and (not (looking-at drawers-re))
- (not (looking-at "^[ \t]*:END:")))))
- (save-excursion
- (list
- (progn
- (re-search-backward drawers-re prev-head t)
- (1+ (point-at-eol)))
- (if (re-search-forward "^[ \t]*:END:" next-head t)
- (1- (point-at-bol))
- next-head)
- 'drawer))))
- ;; Is point strictly in a block, and of which type?
- (blockp
- (save-excursion
- (when (and (org-in-regexps-block-p
- "^[ \t]*#\\+begin_" "^[ \t]*#\\+end_" prev-head)
- (save-excursion
- (beginning-of-line)
- (not (looking-at
- "^[ \t]*#\\+\\(begin\\|end\\)_"))))
- (list
- (progn
- (re-search-backward
- "^[ \t]*#\\+begin_\\(\\S-+\\)" prev-head t)
- (1+ (point-at-eol)))
- (save-match-data
- (if (re-search-forward "^[ \t]*#\\+end_" next-head t)
- (1- (point-at-bol))
- next-head))
- (if (member (downcase (match-string 1))
- org-list-forbidden-blocks)
- 'invalid
- 'block)))))
- ;; Is point in an inlinetask?
- (inlinetaskp
- (when (and (featurep 'org-inlinetask)
- (org-inlinetask-in-task-p)
- (not (looking-at "^\\*+")))
- (save-excursion
- (list
- (progn (org-inlinetask-goto-beginning)
- (1+ (point-at-eol)))
- (progn
- (org-inlinetask-goto-end)
- (forward-line -1)
- (1- (point-at-bol)))
- 'inlinetask))))
- ;; List actual candidates
- (context-list
- (delq nil (list heading drawerp blockp inlinetaskp))))
- ;; Return the closest context around
- (assq (apply 'max (mapcar 'car context-list)) context-list)))))
- (defun org-list-struct ()
- "Return structure of list at point.
- A list structure is an alist where key is point at item, and
- values are:
- 1. indentation,
- 2. bullet with trailing whitespace,
- 3. bullet counter, if any,
- 4. checkbox, if any,
- 5. position at item end,
- 6. description tag, if any.
- Thus the following list, where numbers in parens are
- point-at-bol:
- - [X] first item (1)
- 1. sub-item 1 (18)
- 5. [@5] sub-item 2 (34)
- some other text belonging to first item (55)
- - last item (97)
- + tag :: description (109)
- (131)
- will get the following structure:
- \(\(1 0 \"- \" nil \"[X]\" nil 97\)
- \(18 2 \"1. \" nil nil nil 34\)
- \(34 2 \"5. \" \"5\" nil nil 55\)
- \(97 0 \"- \" nil nil nil 131\)
- \(109 2 \"+ \" nil nil \"tag\" 131\)
- Assume point is at an item."
- (save-excursion
- (beginning-of-line)
- (let* ((case-fold-search t)
- (context (org-list-context))
- (lim-up (car context))
- (lim-down (nth 1 context))
- (text-min-ind 10000)
- (item-re (org-item-re))
- (drawers-re (concat "^[ \t]*:\\("
- (mapconcat 'regexp-quote org-drawers "\\|")
- "\\):[ \t]*$"))
- (inlinetask-re (and (featurep 'org-inlinetask)
- (org-inlinetask-outline-regexp)))
- (beg-cell (cons (point) (org-get-indentation)))
- ind itm-lst itm-lst-2 end-lst end-lst-2 struct
- (assoc-at-point
- (function
- ;; Return association at point.
- (lambda (ind)
- (looking-at org-list-full-item-re)
- (list (point)
- ind
- (match-string-no-properties 1) ; bullet
- (match-string-no-properties 2) ; counter
- (match-string-no-properties 3) ; checkbox
- (match-string-no-properties 4))))) ; description tag
- (end-before-blank
- (function
- ;; Ensure list ends at the first blank line.
- (lambda ()
- (skip-chars-backward " \r\t\n")
- (min (1+ (point-at-eol)) lim-down)))))
- ;; 1. Read list from starting item to its beginning, and save
- ;; top item position and indentation in BEG-CELL. Also store
- ;; ending position of items in END-LST.
- (save-excursion
- (catch 'exit
- (while t
- (let ((ind (+ (or (get-text-property (point) 'original-indentation) 0)
- (org-get-indentation))))
- (cond
- ((<= (point) lim-up)
- ;; At upward limit: if we ended at an item, store it,
- ;; else dimiss useless data recorded above BEG-CELL.
- ;; Jump to part 2.
- (throw 'exit
- (setq itm-lst
- (if (or (not (looking-at item-re))
- (get-text-property (point) 'org-example))
- (memq (assq (car beg-cell) itm-lst) itm-lst)
- (setq beg-cell (cons (point) ind))
- (cons (funcall assoc-at-point ind) itm-lst)))))
- ;; At a verbatim block, go before its beginning. Move
- ;; from eol to ensure `previous-single-property-change'
- ;; will return a value.
- ((get-text-property (point) 'org-example)
- (goto-char (previous-single-property-change
- (point-at-eol) 'org-example nil lim-up))
- (forward-line -1))
- ;; Looking at a list ending regexp. Dismiss useless
- ;; data recorded above BEG-CELL. Jump to part 2.
- ((and (not (eq org-list-ending-method 'indent))
- (looking-at org-list-end-re))
- (throw 'exit
- (setq itm-lst
- (memq (assq (car beg-cell) itm-lst) itm-lst))))
- ;; Skip blocks, drawers, inline tasks, blank lines
- ;; along the way.
- ((looking-at "^[ \t]*#\\+end_")
- (re-search-backward "^[ \t]*#\\+begin_" nil t))
- ((looking-at "^[ \t]*:END:")
- (re-search-backward drawers-re nil t)
- (beginning-of-line))
- ((and inlinetask-re (looking-at inlinetask-re))
- (org-inlinetask-goto-beginning)
- (forward-line -1))
- ((looking-at "^[ \t]*$")
- (forward-line -1))
- ((looking-at item-re)
- ;; Point is at an item. Add data to ITM-LST. It may
- ;; also end a previous item: save it in END-LST. If
- ;; ind is less or equal than BEG-CELL and there is no
- ;; end at this ind or lesser, this item becomes the
- ;; new BEG-CELL.
- (push (funcall assoc-at-point ind) itm-lst)
- (push (cons ind (point)) end-lst)
- (when (or (and (eq org-list-ending-method 'regexp)
- (<= ind (cdr beg-cell)))
- (< ind text-min-ind))
- (setq beg-cell (cons (point) ind)))
- (forward-line -1))
- ;; From there, point is not at an item. Unless ending
- ;; method is `regexp', interpret line's indentation:
- ;; - text at column 0 is necessarily out of any list.
- ;; Dismiss data recorded above BEG-CELL. Jump to
- ;; part 2.
- ;; - any other case, it can possibly be an ending
- ;; position for an item above. Save it and proceed.
- ((eq org-list-ending-method 'regexp) (forward-line -1))
- ((zerop ind)
- (throw 'exit
- (setq itm-lst
- (memq (assq (car beg-cell) itm-lst) itm-lst))))
- (t
- (when (< ind text-min-ind) (setq text-min-ind ind))
- (push (cons ind (point)) end-lst)
- (forward-line -1)))))))
- ;; 2. Read list from starting point to its end, that is until we
- ;; get out of context, or a non-item line is less or equally
- ;; indented that BEG-CELL's cdr. Also store ending position
- ;; of items in END-LST-2.
- (catch 'exit
- (while t
- (let ((ind (+ (or (get-text-property (point) 'original-indentation) 0)
- (org-get-indentation))))
- (cond
- ((>= (point) lim-down)
- ;; At downward limit: this is de facto the end of the
- ;; list. Save point as an ending position, and jump to
- ;; part 3.
- (throw 'exit
- (push (cons 0 (funcall end-before-blank)) end-lst-2)))
- ;; At a verbatim block, move to its end. Point is at bol
- ;; and 'org-example property is set by whole lines:
- ;; `next-single-property-change' always return a value.
- ((get-text-property (point) 'org-example)
- (goto-char
- (next-single-property-change (point) 'org-example nil lim-down)))
- ;; Looking at a list ending regexp. Save point as an
- ;; ending position and jump to part 3.
- ((and (not (eq org-list-ending-method 'indent))
- (looking-at org-list-end-re))
- (throw 'exit (push (cons 0 (point)) end-lst-2)))
- ;; Skip blocks, drawers, inline tasks and blank lines
- ;; along the way
- ((looking-at "^[ \t]*#\\+begin_")
- (re-search-forward "^[ \t]*#\\+end_")
- (forward-line 1))
- ((looking-at drawers-re)
- (re-search-forward "^[ \t]*:END:" nil t)
- (forward-line 1))
- ((and inlinetask-re (looking-at inlinetask-re))
- (org-inlinetask-goto-end))
- ((looking-at "^[ \t]*$")
- (forward-line 1))
- ((looking-at item-re)
- ;; Point is at an item. Add data to ITM-LST-2. It may also
- ;; end a previous item, so save it in END-LST-2.
- (push (funcall assoc-at-point ind) itm-lst-2)
- (push (cons ind (point)) end-lst-2)
- (forward-line 1))
- ;; From there, point is not at an item. If ending method
- ;; is not `regexp', two situations are of interest:
- ;; - ind is lesser or equal than BEG-CELL's. The list is
- ;; over. Store point as an ending position and jump to
- ;; part 3.
- ;; - ind is lesser or equal than previous item's. This
- ;; is an ending position. Store it and proceed.
- ((eq org-list-ending-method 'regexp) (forward-line 1))
- ((<= ind (cdr beg-cell))
- (throw 'exit
- (push (cons 0 (funcall end-before-blank)) end-lst-2)))
- ((<= ind (nth 1 (car itm-lst-2)))
- (push (cons ind (point)) end-lst-2)
- (forward-line 1))
- (t (forward-line 1))))))
- (setq struct (append itm-lst (cdr (nreverse itm-lst-2))))
- (setq end-lst (append end-lst (cdr (nreverse end-lst-2))))
- ;; 3. Correct ill-formed lists by ensuring top item is the least
- ;; indented.
- (let ((min-ind (nth 1 (car struct))))
- (mapc (lambda (item)
- (let ((ind (nth 1 item))
- (bul (nth 2 item)))
- (when (< ind min-ind)
- (setcar (cdr item) min-ind)
- ;; Modify bullet to be sure item will be modified
- (setcar (nthcdr 2 item) (org-trim bul)))))
- struct))
- ;; 4. Associate each item to its end pos.
- (org-list-struct-assoc-end struct end-lst)
- ;; 5. Return STRUCT
- struct)))
- (defun org-list-struct-assoc-end (struct end-list)
- "Associate proper ending point to items in STRUCT.
- END-LIST is a pseudo-alist where car is indentation and cdr is
- ending position.
- This function modifies STRUCT."
- (let ((endings end-list))
- (mapc
- (lambda (elt)
- (let ((pos (car elt))
- (ind (nth 1 elt)))
- ;; Remove end candidates behind current item
- (while (or (<= (cdar endings) pos))
- (pop endings))
- ;; Add end position to item assoc
- (let ((old-end (nthcdr 6 elt))
- (new-end (assoc-default ind endings '<=)))
- (if old-end
- (setcar old-end new-end)
- (setcdr elt (append (cdr elt) (list new-end)))))))
- struct)))
- (defun org-list-prevs-alist (struct)
- "Return alist between item and previous item in STRUCT."
- (let ((item-end-alist (mapcar (lambda (e) (cons (car e) (nth 6 e)))
- struct)))
- (mapcar (lambda (e)
- (let ((prev (car (rassq (car e) item-end-alist))))
- (cons (car e) prev)))
- struct)))
- (defun org-list-parents-alist (struct)
- "Return alist between item and parent in STRUCT."
- (let ((ind-to-ori (list (list (nth 1 (car struct)))))
- (prev-pos (list (caar struct))))
- (cons prev-pos
- (mapcar (lambda (item)
- (let ((pos (car item))
- (ind (nth 1 item))
- (prev-ind (caar ind-to-ori)))
- (push pos prev-pos)
- (cond
- ((> prev-ind ind)
- (setq ind-to-ori
- (member (assq ind ind-to-ori) ind-to-ori))
- (cons pos (cdar ind-to-ori)))
- ((< prev-ind ind)
- (let ((origin (nth 1 prev-pos)))
- (push (cons ind origin) ind-to-ori)
- (cons pos origin)))
- (t (cons pos (cdar ind-to-ori))))))
- (cdr struct)))))
- ;;; Accessors
- (defsubst org-list-get-nth (n key struct)
- "Return the Nth value of KEY in STRUCT."
- (nth n (assq key struct)))
- (defun org-list-set-nth (n key struct new)
- "Set the Nth value of KEY in STRUCT to NEW.
- \nThis function modifies STRUCT."
- (setcar (nthcdr n (assq key struct)) new))
- (defsubst org-list-get-ind (item struct)
- "Return indentation of ITEM in STRUCT."
- (org-list-get-nth 1 item struct))
- (defun org-list-set-ind (item struct ind)
- "Set indentation of ITEM in STRUCT to IND.
- \nThis function modifies STRUCT."
- (org-list-set-nth 1 item struct ind))
- (defsubst org-list-get-bullet (item struct)
- "Return bullet of ITEM in STRUCT."
- (org-list-get-nth 2 item struct))
- (defun org-list-set-bullet (item struct bullet)
- "Set bullet of ITEM in STRUCT to BULLET.
- \nThis function modifies STRUCT."
- (org-list-set-nth 2 item struct bullet))
- (defsubst org-list-get-counter (item struct)
- "Return counter of ITEM in STRUCT."
- (org-list-get-nth 3 item struct))
- (defsubst org-list-get-checkbox (item struct)
- "Return checkbox of ITEM in STRUCT or nil."
- (org-list-get-nth 4 item struct))
- (defun org-list-set-checkbox (item struct checkbox)
- "Set checkbox of ITEM in STRUCT to CHECKBOX.
- \nThis function modifies STRUCT."
- (org-list-set-nth 4 item struct checkbox))
- (defsubst org-list-get-tag (item struct)
- "Return end position of ITEM in STRUCT."
- (org-list-get-nth 5 item struct))
- (defun org-list-get-item-end (item struct)
- "Return end position of ITEM in STRUCT."
- (org-list-get-nth 6 item struct))
- (defun org-list-get-item-end-before-blank (item struct)
- "Return point at end of ITEM in STRUCT, before any blank line.
- Point returned is at end of line."
- (save-excursion
- (goto-char (org-list-get-item-end item struct))
- (skip-chars-backward " \r\t\n")
- (point-at-eol)))
- (defun org-list-get-parent (item struct parents)
- "Return parent of ITEM in STRUCT, or nil.
- PARENTS is the alist of items' parent. See
- `org-list-parents-alist'."
- (let ((parents (or parents (org-list-parents-alist struct))))
- (cdr (assq item parents))))
- (defun org-list-has-child-p (item struct)
- "Return a non-nil value if ITEM in STRUCT has a child.
- Value returned is the position of the first child of ITEM."
- (let ((ind (org-list-get-ind item struct))
- (child-maybe (car (nth 1 (member (assq item struct) struct)))))
- (when (and child-maybe
- (< ind (org-list-get-ind child-maybe struct)))
- child-maybe)))
- (defun org-list-get-next-item (item struct prevs)
- "Return next item in same sub-list as ITEM in STRUCT, or nil.
- PREVS is the alist of previous items. See
- `org-list-prevs-alist'."
- (car (rassq item prevs)))
- (defun org-list-get-prev-item (item struct prevs)
- "Return previous item in same sub-list as ITEM in STRUCT, or nil.
- PREVS is the alist of previous items. See
- `org-list-prevs-alist'."
- (cdr (assq item prevs)))
- (defun org-list-get-subtree (item struct)
- "Return all items with ITEM as a common ancestor or nil.
- STRUCT is the list structure considered."
- (let* ((item-end (org-list-get-item-end item struct))
- (sub-struct (cdr (member (assq item struct) struct)))
- subtree)
- (catch 'exit
- (mapc (lambda (e)
- (let ((pos (car e)))
- (if (< pos item-end) (push pos subtree) (throw 'exit nil))))
- sub-struct))
- (nreverse subtree)))
- (defun org-list-get-all-items (item struct prevs)
- "List of items in the same sub-list as ITEM in STRUCT.
- PREVS is the alist of previous items. See
- `org-list-prevs-alist'."
- (let ((prev-item item)
- (next-item item)
- before-item after-item)
- (while (setq prev-item (org-list-get-prev-item prev-item struct prevs))
- (push prev-item before-item))
- (while (setq next-item (org-list-get-next-item next-item struct prevs))
- (push next-item after-item))
- (append before-item (list item) (nreverse after-item))))
- (defun org-list-get-children (item struct parents)
- "List all children of ITEM in STRUCT, or nil.
- PARENTS is the alist of items' parent. See
- `org-list-parents-alist'."
- (let (all child)
- (while (setq child (car (rassq item parents)))
- (setq parents (cdr (member (assq child parents) parents)))
- (push child all))
- (nreverse all)))
- (defun org-list-get-top-point (struct)
- "Return point at beginning of list.
- STRUCT is the structure of the list."
- (caar struct))
- (defun org-list-get-bottom-point (struct)
- "Return point at bottom of list.
- STRUCT is the structure of the list."
- (apply 'max
- (mapcar (lambda (e) (org-list-get-item-end (car e) struct)) struct)))
- (defun org-list-get-list-begin (item struct prevs)
- "Return point at beginning of sub-list ITEM belongs.
- STRUCT is the structure of the list. PREVS is the alist of
- previous items. See `org-list-prevs-alist'."
- (let ((first-item item) prev-item)
- (while (setq prev-item (org-list-get-prev-item first-item struct prevs))
- (setq first-item prev-item))
- first-item))
- (defalias 'org-list-get-first-item 'org-list-get-list-begin)
- (defun org-list-get-last-item (item struct prevs)
- "Return point at last item of sub-list ITEM belongs.
- STRUCT is the structure of the list. PREVS is the alist of
- previous items. See `org-list-prevs-alist'."
- (let ((last-item item) next-item)
- (while (setq next-item (org-list-get-next-item last-item struct prevs))
- (setq last-item next-item))
- last-item))
- (defun org-list-get-list-end (item struct prevs)
- "Return point at end of sub-list ITEM belongs.
- STRUCT is the structure of the list. PREVS is the alist of
- previous items. See `org-list-prevs-alist'."
- (org-list-get-item-end (org-list-get-last-item item struct prevs) struct))
- (defun org-list-get-list-type (item struct prevs)
- "Return the type of the list containing ITEM as a symbol.
- STRUCT is the structure of the list, as returned by
- `org-list-struct'. PREVS is the alist of previous items. See
- `org-list-prevs-alist'.
- Possible types are `descriptive', `ordered' and `unordered'. The
- type is determined by the first item of the list."
- (let ((first (org-list-get-list-begin item struct prevs)))
- (cond
- ((org-list-get-tag first struct) 'descriptive)
- ((string-match "[[:alnum:]]" (org-list-get-bullet first struct)) 'ordered)
- (t 'unordered))))
- ;;; Searching
- (defun org-list-search-generic (search re bound noerr)
- "Search a string in valid contexts for lists.
- Arguments SEARCH, RE, BOUND and NOERR are similar to those in
- `re-search-forward'."
- (catch 'exit
- (let ((origin (point)))
- (while t
- ;; 1. No match: return to origin or bound, depending on NOERR.
- (unless (funcall search re bound noerr)
- (throw 'exit (and (goto-char (if (memq noerr '(t nil)) origin bound))
- nil)))
- ;; 2. Match in an `invalid' context: continue searching. Else,
- ;; return point.
- (unless (eq (org-list-context) 'invalid) (throw 'exit (point)))))))
- (defun org-list-search-backward (regexp &optional bound noerror)
- "Like `re-search-backward' but stop only where lists are recognized.
- Arguments REGEXP, BOUND and NOERROR are similar to those used in
- `re-search-backward'."
- (org-list-search-generic #'re-search-backward
- regexp (or bound (point-min)) noerror))
- (defun org-list-search-forward (regexp &optional bound noerror)
- "Like `re-search-forward' but stop only where lists are recognized.
- Arguments REGEXP, BOUND and NOERROR are similar to those used in
- `re-search-forward'."
- (org-list-search-generic #'re-search-forward
- regexp (or bound (point-max)) noerror))
- ;;; Methods on structures
- (defsubst org-list-bullet-string (bullet)
- "Return BULLET with the correct number of whitespaces.
- It determines the number of whitespaces to append by looking at
- `org-list-two-spaces-after-bullet-regexp'."
- (save-match-data
- (let ((spaces (if (and org-list-two-spaces-after-bullet-regexp
- (string-match
- org-list-two-spaces-after-bullet-regexp bullet))
- " "
- " ")))
- (string-match "\\S-+\\([ \t]*\\)" bullet)
- (replace-match spaces nil nil bullet 1))))
- (defun org-list-separating-blank-lines-number (pos struct prevs)
- "Return number of blank lines that should separate items in list.
- POS is the position at item beginning to be considered. STRUCT is
- the list structure. PREVS is the alist of previous items. See
- `org-list-prevs-alist'.
- Assume point is at item's beginning. If the item is alone, apply
- some heuristics to guess the result."
- (save-excursion
- (let ((insert-blank-p
- (cdr (assq 'plain-list-item org-blank-before-new-entry)))
- usr-blank)
- (cond
- ;; Trivial cases where there should be none.
- ((or (and (not (eq org-list-ending-method 'indent))
- org-empty-line-terminates-plain-lists)
- (not insert-blank-p)) 0)
- ;; When `org-blank-before-new-entry' says so, it is 1.
- ((eq insert-blank-p t) 1)
- ;; plain-list-item is 'auto. Count blank lines separating
- ;; neighbours items in list.
- (t (let ((next-p (org-list-get-next-item (point) struct prevs)))
- (cond
- ;; Is there a next item?
- (next-p (goto-char next-p)
- (org-back-over-empty-lines))
- ;; Is there a previous item?
- ((org-list-get-prev-item (point) struct prevs)
- (org-back-over-empty-lines))
- ;; User inserted blank lines, trust him
- ((and (> pos (org-list-get-item-end-before-blank pos struct))
- (> (save-excursion
- (goto-char pos)
- (skip-chars-backward " \t")
- (setq usr-blank (org-back-over-empty-lines))) 0))
- usr-blank)
- ;; Are there blank lines inside the item ?
- ((save-excursion
- (org-list-search-forward
- "^[ \t]*$" (org-list-get-item-end-before-blank pos struct) t))
- 1)
- ;; No parent: no blank line.
- (t 0))))))))
- (defun org-list-insert-item (pos struct prevs &optional checkbox after-bullet)
- "Insert a new list item at POS.
- If POS is before first character after bullet of the item, the
- new item will be created before the current one.
- STRUCT is the list structure, as returned by `org-list-struct'.
- PREVS is the the alist of previous items. See
- `org-list-prevs-alist'.
- 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.
- Return the new structure of the list."
- (let ((case-fold-search t))
- ;; 1. Get information about list: structure, usual helper
- ;; functions, position of point with regards to item start
- ;; (BEFOREP), blank lines number separating items (BLANK-NB),
- ;; position of split (POS) if we're allowed to (SPLIT-LINE-P).
- (let* ((item (goto-char (org-list-get-item-begin)))
- (item-end (org-list-get-item-end item struct))
- (item-end-no-blank (org-list-get-item-end-before-blank item struct))
- (beforep (and (looking-at org-list-full-item-re)
- (<= pos (match-end 0))))
- (split-line-p (org-get-alist-option org-M-RET-may-split-line 'item))
- (blank-nb (org-list-separating-blank-lines-number
- item struct prevs))
- ;; 2. Build the new item to be created. Concatenate same
- ;; bullet as item, checkbox, text AFTER-BULLET if
- ;; provided, and text cut from point to end of item
- ;; (TEXT-CUT) to form item's BODY. TEXT-CUT depends on
- ;; BEFOREP and SPLIT-LINE-P. The difference of size
- ;; between what was cut and what was inserted in buffer
- ;; is stored in SIZE-OFFSET.
- (ind (org-list-get-ind item struct))
- (bullet (org-list-bullet-string (org-list-get-bullet item struct)))
- (box (when checkbox "[ ]"))
- (text-cut
- (and (not beforep) split-line-p
- (progn
- (goto-char pos)
- (skip-chars-backward " \r\t\n")
- (setq pos (point))
- (delete-and-extract-region pos item-end-no-blank))))
- (body (concat bullet (when box (concat box " ")) after-bullet
- (or (and text-cut
- (if (string-match "\\`[ \t]+" text-cut)
- (replace-match "" t t text-cut)
- text-cut))
- "")))
- (item-sep (make-string (1+ blank-nb) ?\n))
- (item-size (+ ind (length body) (length item-sep)))
- (size-offset (- item-size (length text-cut))))
- ;; 4. Insert effectively item into buffer
- (goto-char item)
- (org-indent-to-column ind)
- (insert body)
- (insert item-sep)
- ;; 5. Add new item to STRUCT.
- (mapc (lambda (e)
- (let ((p (car e))
- (end (nth 6 e)))
- (cond
- ;; Before inserted item, positions don't change but
- ;; an item ending after insertion has its end shifted
- ;; by SIZE-OFFSET.
- ((< p item)
- (when (> end item) (setcar (nthcdr 6 e) (+ end size-offset))))
- ;; Trivial cases where current item isn't split in
- ;; two. Just shift every item after new one by
- ;; ITEM-SIZE.
- ((or beforep (not split-line-p))
- (setcar e (+ p item-size))
- (setcar (nthcdr 6 e) (+ end item-size)))
- ;; Item is split in two: elements before POS are just
- ;; shifted by ITEM-SIZE. In the case item would end
- ;; after split POS, ending is only shifted by
- ;; SIZE-OFFSET.
- ((< p pos)
- (setcar e (+ p item-size))
- (if (< end pos)
- (setcar (nthcdr 6 e) (+ end item-size))
- (setcar (nthcdr 6 e) (+ end size-offset))))
- ;; Elements after POS are moved into new item. Length
- ;; of ITEM-SEP has to be removed as ITEM-SEP
- ;; doesn't appear in buffer yet.
- ((< p item-end)
- (setcar e (+ p size-offset (- item pos (length item-sep))))
- (if (= end item-end)
- (setcar (nthcdr 6 e) (+ item item-size))
- (setcar (nthcdr 6 e)
- (+ end size-offset
- (- item pos (length item-sep))))))
- ;; Elements at ITEM-END or after are only shifted by
- ;; SIZE-OFFSET.
- (t (setcar e (+ p size-offset))
- (setcar (nthcdr 6 e) (+ end size-offset))))))
- struct)
- (push (list item ind bullet nil box nil (+ item item-size)) struct)
- (setq struct (sort struct (lambda (e1 e2) (< (car e1) (car e2)))))
- ;; 6. If not BEFOREP, new item must appear after ITEM, so
- ;; exchange ITEM with the next item in list. Position cursor
- ;; after bullet, counter, checkbox, and label.
- (if beforep
- (goto-char item)
- (setq struct (org-list-exchange-items item (+ item item-size) struct))
- (goto-char (org-list-get-next-item
- item struct (org-list-prevs-alist struct))))
- struct)))
- (defun org-list-exchange-items (beg-A beg-B struct)
- "Swap item starting at BEG-A with item starting at BEG-B in STRUCT.
- Blank lines at the end of items are left in place. Return the new
- structure after the changes.
- Assume BEG-A is lesser than BEG-B and that BEG-A and BEG-B belong
- to the same sub-list.
- This function modifies STRUCT."
- (save-excursion
- (let* ((end-A-no-blank (org-list-get-item-end-before-blank beg-A struct))
- (end-B-no-blank (org-list-get-item-end-before-blank beg-B struct))
- (end-A (org-list-get-item-end beg-A struct))
- (end-B (org-list-get-item-end beg-B struct))
- (size-A (- end-A-no-blank beg-A))
- (size-B (- end-B-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))
- (sub-A (cons beg-A (org-list-get-subtree beg-A struct)))
- (sub-B (cons beg-B (org-list-get-subtree beg-B struct))))
- ;; 1. Move effectively items in buffer.
- (goto-char beg-A)
- (delete-region beg-A end-B-no-blank)
- (insert (concat body-B between-A-no-blank-and-B body-A))
- ;; 2. Now modify struct. No need to re-read the list, the
- ;; transformation is just a shift of positions. Some special
- ;; attention is required for items ending at END-A and END-B
- ;; as empty spaces are not moved there. In others words, item
- ;; BEG-A will end with whitespaces that were at the end of
- ;; BEG-B and the same applies to BEG-B.
- (mapc (lambda (e)
- (let ((pos (car e)))
- (cond
- ((< pos beg-A))
- ((memq pos sub-A)
- (let ((end-e (nth 6 e)))
- (setcar e (+ pos (- end-B-no-blank end-A-no-blank)))
- (setcar (nthcdr 6 e)
- (+ end-e (- end-B-no-blank end-A-no-blank)))
- (when (= end-e end-A) (setcar (nthcdr 6 e) end-B))))
- ((memq pos sub-B)
- (let ((end-e (nth 6 e)))
- (setcar e (- (+ pos beg-A) beg-B))
- (setcar (nthcdr 6 e) (+ end-e (- beg-A beg-B)))
- (when (= end-e end-B)
- (setcar (nthcdr 6 e)
- (+ beg-A size-B (- end-A end-A-no-blank))))))
- ((< pos beg-B)
- (let ((end-e (nth 6 e)))
- (setcar e (+ pos (- size-B size-A)))
- (setcar (nthcdr 6 e) (+ end-e (- size-B size-A))))))))
- struct)
- (sort struct (lambda (e1 e2) (< (car e1) (car e2)))))))
- (defun org-list-struct-outdent (start end struct parents)
- "Outdent items between START and END in structure STRUCT.
- PARENTS is the alist of items' parents. See
- `org-list-parents-alist'.
- START is included, END excluded."
- (let* (acc
- (out (lambda (cell)
- (let* ((item (car cell))
- (parent (cdr cell)))
- (cond
- ;; Item not yet in zone: keep association
- ((< item start) cell)
- ;; Item out of zone: follow associations in acc
- ((>= item end)
- (let ((convert (and parent (assq parent acc))))
- (if convert (cons item (cdr convert)) cell)))
- ;; Item has no parent: error
- ((not parent)
- (error "Cannot outdent top-level items"))
- ;; Parent is outdented: keep association
- ((>= parent start)
- (push (cons parent item) acc) cell)
- (t
- ;; Parent isn't outdented: reparent to grand-parent
- (let ((grand-parent (org-list-get-parent
- parent struct parents)))
- (push (cons parent item) acc)
- (cons item grand-parent))))))))
- (mapcar out parents)))
- (defun org-list-struct-indent (start end struct parents prevs)
- "Indent items between START and END in structure STRUCT.
- PARENTS is the alist of parents. See `org-list-parents-alist'.
- PREVS is the alist of previous items. See `org-list-prevs-alist'.
- START is included and END excluded.
- STRUCT may be modified if `org-list-demote-modify-bullet' matches
- bullets between START and END."
- (let* (acc
- (set-assoc (lambda (cell) (push cell acc) cell))
- (change-bullet-maybe
- (function
- (lambda (item)
- (let* ((bul (org-trim (org-list-get-bullet item struct)))
- (new-bul-p (cdr (assoc bul org-list-demote-modify-bullet))))
- (when new-bul-p (org-list-set-bullet item struct new-bul-p))))))
- (ind
- (lambda (cell)
- (let* ((item (car cell))
- (parent (cdr cell)))
- (cond
- ;; Item not yet in zone: keep association
- ((< item start) cell)
- ((>= item end)
- ;; Item out of zone: follow associations in acc
- (let ((convert (assq parent acc)))
- (if convert (cons item (cdr convert)) cell)))
- (t
- ;; Item is in zone...
- (let ((prev (org-list-get-prev-item item struct prevs)))
- ;; Check if bullet needs to be changed
- (funcall change-bullet-maybe item)
- (cond
- ;; First item indented but not parent: error
- ((and (not prev) (< parent start))
- (error "Cannot indent the first item of a list"))
- ;; First item and parent indented: keep same parent
- ((not prev) (funcall set-assoc cell))
- ;; Previous item not indented: reparent to it
- ((< prev start) (funcall set-assoc (cons item prev)))
- ;; Previous item indented: reparent like it
- (t
- (funcall set-assoc
- (cons item (cdr (assq prev acc)))))))))))))
- (mapcar ind parents)))
- ;;; Repairing structures
- (defun org-list-use-alpha-bul-p (first struct prevs)
- "Can list starting at FIRST use alphabetical bullets?
- STRUCT is list structure. See `org-list-struct'. PREVS is the
- alist of previous items. See `org-list-prevs-alist'."
- (and org-alphabetical-lists
- (catch 'exit
- (let ((item first) (ascii 64) (case-fold-search nil))
- ;; Pretend that bullets are uppercase and check if alphabet
- ;; is sufficient, taking counters into account.
- (while item
- (let ((bul (org-list-get-bullet item struct))
- (count (org-list-get-counter item struct)))
- ;; Virtually determine current bullet
- (if (and count (string-match "[a-zA-Z]" count))
- ;; Counters are not case-sensitive.
- (setq ascii (string-to-char (upcase count)))
- (setq ascii (1+ ascii)))
- ;; Test if bullet would be over z or Z.
- (if (> ascii 90)
- (throw 'exit nil)
- (setq item (org-list-get-next-item item struct prevs)))))
- ;; All items checked. All good.
- t))))
- (defun org-list-inc-bullet-maybe (bullet)
- "Increment BULLET if applicable."
- (let ((case-fold-search nil))
- (cond
- ;; Num bullet: increment it.
- ((string-match "[0-9]+" bullet)
- (replace-match
- (number-to-string (1+ (string-to-number (match-string 0 bullet))))
- nil nil bullet))
- ;; Alpha bullet: increment it.
- ((string-match "[A-Za-z]" bullet)
- (replace-match
- (char-to-string (1+ (string-to-char (match-string 0 bullet))))
- nil nil bullet))
- ;; Unordered bullet: leave it.
- (t bullet))))
- (defun org-list-struct-fix-bul (struct prevs)
- "Verify and correct bullets for every association in STRUCT.
- PREVS is the alist of previous items. See
- `org-list-prevs-alist'.
- This function modifies STRUCT."
- (let ((case-fold-search nil)
- (fix-bul
- (function
- ;; Set bullet of ITEM in STRUCT, depending on the type of
- ;; first item of the list, the previous bullet and counter
- ;; if any.
- (lambda (item)
- (let* ((prev (org-list-get-prev-item item struct prevs))
- (prev-bul (and prev (org-list-get-bullet prev struct)))
- (counter (org-list-get-counter item struct))
- (bullet (org-list-get-bullet item struct))
- (alphap (and (not prev)
- (org-list-use-alpha-bul-p item struct prevs))))
- (org-list-set-bullet
- item struct
- (org-list-bullet-string
- (cond
- ;; Alpha counter in alpha list: use counter.
- ((and prev counter
- (string-match "[a-zA-Z]" counter)
- (string-match "[a-zA-Z]" prev-bul))
- ;; Use cond to be sure `string-match' is used in
- ;; both cases.
- (let ((real-count
- (cond
- ((string-match "[a-z]" prev-bul) (downcase counter))
- ((string-match "[A-Z]" prev-bul) (upcase counter)))))
- (replace-match real-count nil nil prev-bul)))
- ;; Num counter in a num list: use counter.
- ((and prev counter
- (string-match "[0-9]+" counter)
- (string-match "[0-9]+" prev-bul))
- (replace-match counter nil nil prev-bul))
- ;; No counter: increase, if needed, previous bullet.
- (prev
- (org-list-inc-bullet-maybe (org-list-get-bullet prev struct)))
- ;; Alpha counter at first item: use counter.
- ((and counter (org-list-use-alpha-bul-p item struct prevs)
- (string-match "[A-Za-z]" counter)
- (string-match "[A-Za-z]" bullet))
- (let ((real-count
- (cond
- ((string-match "[a-z]" bullet) (downcase counter))
- ((string-match "[A-Z]" bullet) (upcase counter)))))
- (replace-match real-count nil nil bullet)))
- ;; Num counter at first item: use counter.
- ((and counter
- (string-match "[0-9]+" counter)
- (string-match "[0-9]+" bullet))
- (replace-match counter nil nil bullet))
- ;; First bullet is alpha uppercase: use "A".
- ((and alphap (string-match "[A-Z]" bullet))
- (replace-match "A" nil nil bullet))
- ;; First bullet is alpha lowercase: use "a".
- ((and alphap (string-match "[a-z]" bullet))
- (replace-match "a" nil nil bullet))
- ;; First bullet is num: use "1".
- ((string-match "\\([0-9]+\\|[A-Za-z]\\)" bullet)
- (replace-match "1" nil nil bullet))
- ;; Not an ordered list: keep bullet.
- (t bullet)))))))))
- (mapc fix-bul (mapcar 'car struct))))
- (defun org-list-struct-fix-ind (struct parents &optional bullet-size)
- "Verify and correct indentation for every association in STRUCT.
- PARENTS is the alist of items' parents. See
- `org-list-parents-alist'.
- If numeric optional argument BULLET-SIZE is set, assume all
- bullets in list have this length to determine new indentation.
- This function modifies STRUCT."
- (let* ((ancestor (org-list-get-top-point struct))
- (top-ind (org-list-get-ind ancestor struct))
- (new-ind
- (lambda (item)
- (let ((parent (org-list-get-parent item struct parents)))
- (if parent
- ;; Indent like parent + length of parent's bullet
- (org-list-set-ind
- item struct (+ (or bullet-size
- (length
- (org-list-get-bullet parent struct)))
- (org-list-get-ind parent struct)))
- ;; If no parent, indent like top-point
- (org-list-set-ind item struct top-ind))))))
- (mapc new-ind (mapcar 'car (cdr struct)))))
- (defun org-list-struct-fix-box (struct parents prevs &optional ordered)
- "Verify and correct checkboxes for every association in STRUCT.
- PARENTS is the alist of items' parents. See
- `org-list-parents-alist'. PREVS is the alist of previous items.
- See `org-list-prevs-alist'.
- If ORDERED is non-nil, a checkbox can only be checked when every
- checkbox before it is checked too. If there was an attempt to
- break this rule, the function will return the blocking item. In
- all others cases, the return value will be nil.
- This function modifies STRUCT."
- (let ((all-items (mapcar 'car struct))
- (set-parent-box
- (function
- (lambda (item)
- (let* ((box-list
- (mapcar (lambda (child)
- (org-list-get-checkbox child struct))
- (org-list-get-children item struct parents))))
- (org-list-set-checkbox
- item struct
- (cond
- ((and (member "[ ]" box-list) (member "[X]" box-list)) "[-]")
- ((member "[-]" box-list) "[-]")
- ((member "[X]" box-list) "[X]")
- ((member "[ ]" box-list) "[ ]")
- ;; parent has no boxed child: leave box as-is
- (t (org-list-get-checkbox item struct))))))))
- parent-list)
- ;; 1. List all parents with a checkbox
- (mapc
- (lambda (e)
- (let* ((parent (org-list-get-parent e struct parents))
- (parent-box-p (org-list-get-checkbox parent struct)))
- (when (and parent-box-p (not (memq parent parent-list)))
- (push parent parent-list))))
- all-items)
- ;; 2. Sort those parents by decreasing indentation
- (setq parent-list (sort parent-list
- (lambda (e1 e2)
- (> (org-list-get-ind e1 struct)
- (org-list-get-ind e2 struct)))))
- ;; 3. For each parent, get all children's checkboxes to determine
- ;; and set its checkbox accordingly
- (mapc set-parent-box parent-list)
- ;; 4. If ORDERED is set, see if we need to uncheck some boxes
- (when ordered
- (let* ((box-list
- (mapcar (lambda (e) (org-list-get-checkbox e struct)) all-items))
- (after-unchecked (member "[ ]" box-list)))
- ;; there are boxes checked after an unchecked one: fix that
- (when (member "[X]" after-unchecked)
- (let ((index (- (length struct) (length after-unchecked))))
- (mapc (lambda (e) (org-list-set-checkbox e struct "[ ]"))
- (nthcdr index all-items))
- ;; Verify once again the structure, without ORDERED
- (org-list-struct-fix-box struct parents prevs nil)
- ;; return blocking item
- (nth index all-items)))))))
- (defun org-list-struct-apply-struct (struct old-struct)
- "Apply modifications to list so it mirrors STRUCT.
- OLD-STRUCT is the structure before any modifications. Thus, the
- function is smart enough to modify only parts of buffer which
- have changed.
- Initial position of cursor is restored after the changes."
- (let* ((pos (copy-marker (point)))
- (inlinetask-re (and (featurep 'org-inlinetask)
- (org-inlinetask-outline-regexp)))
- (item-re (org-item-re))
- (shift-body-ind
- (function
- ;; Shift the indentation between END and BEG by DELTA.
- ;; Start from the line before END.
- (lambda (end beg delta)
- (goto-char end)
- (skip-chars-backward " \r\t\n")
- (beginning-of-line)
- (while (or (> (point) beg)
- (and (= (point) beg)
- (not (looking-at item-re))))
- (cond
- ;; Skip inline tasks
- ((and inlinetask-re (looking-at inlinetask-re))
- (org-inlinetask-goto-beginning))
- ;; Shift only non-empty lines
- ((org-looking-at-p "^[ \t]*\\S-")
- (let ((i (org-get-indentation)))
- (org-indent-line-to (+ i delta)))))
- (forward-line -1)))))
- (modify-item
- (function
- ;; Replace ITEM first line elements with new elements from
- ;; STRUCT, if appropriate.
- (lambda (item)
- (goto-char item)
- (let* ((new-ind (org-list-get-ind item struct))
- (old-ind (org-get-indentation))
- (new-bul (org-list-bullet-string
- (org-list-get-bullet item struct)))
- (old-bul (org-list-get-bullet item old-struct))
- (new-box (org-list-get-checkbox item struct)))
- (looking-at org-list-full-item-re)
- ;; a. Replace bullet
- (unless (equal old-bul new-bul)
- (replace-match new-bul nil nil nil 1))
- ;; b. Replace checkbox
- (cond
- ((and new-box
- (save-match-data (org-at-item-description-p))
- (cdr (assq 'checkbox org-list-automatic-rules)))
- (message "Cannot add a checkbox to a description list item"))
- ((equal (match-string 3) new-box))
- ((and (match-string 3) new-box)
- (replace-match new-box nil nil nil 3))
- ((match-string 3)
- (goto-char (or (match-end 2) (match-end 1)))
- (looking-at "\\[[ X-]\\][ \t]+")
- (replace-match ""))
- (t (goto-char (or (match-end 2) (match-end 1)))
- (insert (concat new-box " "))))
- ;; c. Indent item to appropriate column
- (unless (= new-ind old-ind)
- (delete-region (goto-char (point-at-bol))
- (progn (skip-chars-forward " \t") (point)))
- (indent-to new-ind)))))))
- ;; 1. First get list of items and position endings. We maintain
- ;; two alists: ITM-SHIFT, determining indentation shift needed
- ;; at item, and END-POS, a pseudo-alist where key is ending
- ;; position and value point
- (let (end-list acc-end itm-shift all-ends sliced-struct)
- (mapc (lambda (e)
- (let* ((pos (car e))
- (ind-pos (org-list-get-ind pos struct))
- (ind-old (org-list-get-ind pos old-struct))
- (bul-pos (org-list-get-bullet pos struct))
- (bul-old (org-list-get-bullet pos old-struct))
- (ind-shift (- (+ ind-pos (length bul-pos))
- (+ ind-old (length bul-old))))
- (end-pos (org-list-get-item-end pos old-struct)))
- (push (cons pos ind-shift) itm-shift)
- (unless (assq end-pos old-struct)
- ;; To determine real ind of an ending position that is
- ;; not at an item, we have to find the item it belongs
- ;; to: it is the last item (ITEM-UP), whose ending is
- ;; further than the position we're interested in.
- (let ((item-up (assoc-default end-pos acc-end '>)))
- (push (cons end-pos item-up) end-list)))
- (push (cons end-pos pos) acc-end)))
- old-struct)
- ;; 2. Slice the items into parts that should be shifted by the
- ;; same amount of indentation. The slices are returned in
- ;; reverse order so changes modifying buffer do not change
- ;; positions they refer to.
- (setq all-ends (sort (append (mapcar 'car itm-shift)
- (org-uniquify (mapcar 'car end-list)))
- '<))
- (while (cdr all-ends)
- (let* ((up (pop all-ends))
- (down (car all-ends))
- (ind (if (assq up struct)
- (cdr (assq up itm-shift))
- (cdr (assq (cdr (assq up end-list)) itm-shift)))))
- (push (list down up ind) sliced-struct)))
- ;; 3. Shift each slice in buffer, provided delta isn't 0, from
- ;; end to beginning. Take a special action when beginning is
- ;; at item bullet.
- (mapc (lambda (e)
- (unless (zerop (nth 2 e)) (apply shift-body-ind e))
- (let* ((beg (nth 1 e))
- (cell (assq beg struct)))
- (unless (or (not cell) (equal cell (assq beg old-struct)))
- (funcall modify-item beg))))
- sliced-struct))
- ;; 4. Go back to initial position
- (goto-char pos)))
- (defun org-list-write-struct (struct parents)
- "Verify bullets, checkboxes, indentation in STRUCT and apply it to buffer.
- PARENTS is the alist of items' parents. See
- `org-list-parents-alist'."
- ;; Order of functions matters here: checkboxes and endings need
- ;; correct indentation to be set, and indentation needs correct
- ;; bullets.
- ;;
- ;; 0. Save a copy of structure before modifications
- (let ((old-struct (copy-tree struct)))
- ;; 1. Set a temporary, but coherent with PARENTS, indentation in
- ;; order to get items endings and bullets properly
- (org-list-struct-fix-ind struct parents 2)
- ;; 2. Get pseudo-alist of ending positions and sort it by position.
- ;; Then associate them to the structure.
- (let (end-list acc-end)
- (mapc (lambda (e)
- (let* ((pos (car e))
- (ind-pos (org-list-get-ind pos struct))
- (end-pos (org-list-get-item-end pos struct)))
- (unless (assq end-pos struct)
- ;; To determine real ind of an ending position that is
- ;; not at an item, we have to find the item it belongs
- ;; to: it is the last item (ITEM-UP), whose ending is
- ;; further than the position we're interested in.
- (let ((item-up (assoc-default end-pos acc-end '>)))
- (push (cons
- ;; Else part is for the bottom point
- (if item-up (+ (org-list-get-ind item-up struct) 2) 0)
- end-pos)
- end-list)))
- (push (cons ind-pos pos) end-list)
- (push (cons end-pos pos) acc-end)))
- struct)
- (setq end-list (sort end-list (lambda (e1 e2) (< (cdr e1) (cdr e2)))))
- (org-list-struct-assoc-end struct end-list))
- ;; 3. Get bullets right
- (let ((prevs (org-list-prevs-alist struct)))
- (org-list-struct-fix-bul struct prevs)
- ;; 4. Now get real indentation
- (org-list-struct-fix-ind struct parents)
- ;; 5. Eventually fix checkboxes
- (org-list-struct-fix-box struct parents prevs))
- ;; 6. Apply structure modifications to buffer
- (org-list-struct-apply-struct struct old-struct)))
- ;;; Misc Tools
- (defun org-apply-on-list (function init-value &rest args)
- "Call FUNCTION on each item of the list at point.
- FUNCTION must be called with at least one argument: INIT-VALUE,
- that will contain the value returned by the function at the
- previous item, plus ARGS extra arguments.
- FUNCTION is applied on items in reverse order.
- 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."
- (let* ((struct (org-list-struct))
- (prevs (org-list-prevs-alist struct))
- (item (copy-marker (point-at-bol)))
- (all (org-list-get-all-items (marker-position item) struct prevs))
- (value init-value))
- (mapc (lambda (e)
- (goto-char e)
- (setq value (apply function value args)))
- (nreverse all))
- (goto-char item)
- value))
- (defun org-list-set-item-visibility (item struct view)
- "Set visibility of ITEM in STRUCT.
- Symbol VIEW determines visibility. Possible values are: `folded',
- `children' or `subtree'. See `org-cycle' for more information."
- (cond
- ((eq view 'folded)
- (let ((item-end (org-list-get-item-end-before-blank item struct)))
- ;; Hide from eol
- (outline-flag-region (save-excursion (goto-char item) (point-at-eol))
- item-end t)))
- ((eq view 'children)
- ;; First show everything.
- (org-list-set-item-visibility item struct 'subtree)
- ;; Then fold every child.
- (let* ((parents (org-list-parents-alist struct))
- (children (org-list-get-children item struct parents)))
- (mapc (lambda (e)
- (org-list-set-item-visibility e struct 'folded))
- children)))
- ((eq view 'subtree)
- ;; Show everything
- (let ((item-end (org-list-get-item-end item struct)))
- (outline-flag-region item item-end nil)))))
- (defun org-list-item-body-column (item)
- "Return column where body of ITEM should start."
- (let (bpos bcol tpos tcol)
- (save-excursion
- (goto-char item)
- (looking-at "[ \t]*\\(\\S-+\\)\\(.*[ \t]+::\\)?[ \t]+")
- (setq bpos (match-beginning 1) tpos (match-end 0)
- bcol (progn (goto-char bpos) (current-column))
- tcol (progn (goto-char tpos) (current-column)))
- (when (> tcol (+ bcol org-description-max-indent))
- (setq tcol (+ bcol 5))))
- tcol))
- ;;; Interactive functions
- (defalias 'org-list-get-item-begin 'org-in-item-p)
- (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."
- (interactive)
- (let ((begin (org-in-item-p)))
- (if begin (goto-char begin) (error "Not in an item"))))
- (defun org-beginning-of-item-list ()
- "Go to the beginning item of the current list or sublist.
- Return an error if not in a list."
- (interactive)
- (let ((begin (org-in-item-p)))
- (if (not begin)
- (error "Not in an item")
- (goto-char begin)
- (let* ((struct (org-list-struct))
- (prevs (org-list-prevs-alist struct)))
- (goto-char (org-list-get-list-begin begin struct prevs))))))
- (defun org-end-of-item-list ()
- "Go to the end of the current list or sublist.
- If the cursor in not in an item, throw an error."
- (interactive)
- (let ((begin (org-in-item-p)))
- (if (not begin)
- (error "Not in an item")
- (goto-char begin)
- (let* ((struct (org-list-struct))
- (prevs (org-list-prevs-alist struct)))
- (goto-char (org-list-get-list-end begin struct prevs))))))
- (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 ((begin (org-in-item-p)))
- (if (not begin)
- (error "Not in an item")
- (goto-char begin)
- (let ((struct (org-list-struct)))
- (goto-char (org-list-get-item-end begin struct))))))
- (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 ((begin (org-in-item-p)))
- (if (not begin)
- (error "Not in an item")
- (goto-char begin)
- (let* ((struct (org-list-struct))
- (prevs (org-list-prevs-alist struct))
- (prevp (org-list-get-prev-item begin struct prevs)))
- (if prevp (goto-char prevp) (error "On first item"))))))
- (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 ((begin (org-in-item-p)))
- (if (not begin)
- (error "Not in an item")
- (goto-char begin)
- (let* ((struct (org-list-struct))
- (prevs (org-list-prevs-alist struct))
- (prevp (org-list-get-next-item begin struct prevs)))
- (if prevp (goto-char prevp) (error "On last item"))))))
- (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)
- (unless (org-at-item-p) (error "Not at an item"))
- (let* ((pos (point))
- (col (current-column))
- (actual-item (point-at-bol))
- (struct (org-list-struct))
- (prevs (org-list-prevs-alist struct))
- (next-item (org-list-get-next-item (point-at-bol) struct prevs)))
- (if (not next-item)
- (progn
- (goto-char pos)
- (error "Cannot move this item further down"))
- (setq struct
- (org-list-exchange-items actual-item next-item struct))
- ;; Use a short variation of `org-list-write-struct' as there's
- ;; no need to go through all the steps.
- (let ((old-struct (copy-tree struct))
- (prevs (org-list-prevs-alist struct))
- (parents (org-list-parents-alist struct)))
- (org-list-struct-fix-bul struct prevs)
- (org-list-struct-fix-ind struct parents)
- (org-list-struct-apply-struct struct old-struct)
- (goto-char (org-list-get-next-item (point-at-bol) struct prevs)))
- (org-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)
- (unless (org-at-item-p) (error "Not at an item"))
- (let* ((pos (point))
- (col (current-column))
- (actual-item (point-at-bol))
- (struct (org-list-struct))
- (prevs (org-list-prevs-alist struct))
- (prev-item (org-list-get-prev-item (point-at-bol) struct prevs)))
- (if (not prev-item)
- (progn
- (goto-char pos)
- (error "Cannot move this item further up"))
- (setq struct
- (org-list-exchange-items prev-item actual-item struct))
- ;; Use a short variation of `org-list-write-struct' as there's
- ;; no need to go through all the steps.
- (let ((old-struct (copy-tree struct))
- (prevs (org-list-prevs-alist struct))
- (parents (org-list-parents-alist struct)))
- (org-list-struct-fix-bul struct prevs)
- (org-list-struct-fix-ind struct parents)
- (org-list-struct-apply-struct struct old-struct))
- (org-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.
- If CHECKBOX is non-nil, add a checkbox next to the bullet.
- Return t when things worked, nil when we are not in an item, or
- item is invisible."
- (let ((itemp (org-in-item-p))
- (pos (point)))
- ;; If cursor isn't is a list or if list is invisible, return nil.
- (unless (or (not itemp)
- (save-excursion
- (goto-char itemp)
- (org-invisible-p)))
- (if (save-excursion
- (goto-char itemp)
- (org-at-item-timer-p))
- ;; Timer list: delegate to `org-timer-item'.
- (progn (org-timer-item) t)
- (goto-char itemp)
- (let* ((struct (org-list-struct))
- (prevs (org-list-prevs-alist struct))
- ;; If we're in a description list, ask for the new term.
- (desc (when (org-list-get-tag itemp struct)
- (concat (read-string "Term: ") " :: ")))
- ;; Don't insert a checkbox if checkbox rule is applied
- ;; and it is a description item.
- (checkp (and checkbox
- (or (not desc)
- (not (cdr (assq 'checkbox
- org-list-automatic-rules)))))))
- (setq struct
- (org-list-insert-item pos struct prevs checkp desc))
- (org-list-write-struct struct (org-list-parents-alist struct))
- (when checkp (org-update-checkbox-count-maybe))
- (looking-at org-list-full-item-re)
- (goto-char (match-end 0))
- t)))))
- (defun org-list-repair ()
- "Make sure all items are correctly indented, with the right bullet.
- This function scans the list at point, along with any sublist."
- (interactive)
- (unless (org-at-item-p) (error "This is not a list"))
- (let* ((struct (org-list-struct))
- (parents (org-list-parents-alist struct)))
- (org-list-write-struct struct parents)))
- (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 valid string, use that as the new bullet. If WHICH
- is an integer, 0 means `-', 1 means `+' etc. If WHICH is
- `previous', cycle backwards."
- (interactive "P")
- (unless (org-at-item-p) (error "Not at an item"))
- (save-excursion
- (beginning-of-line)
- (let* ((struct (org-list-struct))
- (parents (org-list-parents-alist struct))
- (prevs (org-list-prevs-alist struct))
- (list-beg (org-list-get-first-item (point) struct prevs))
- (bullet (org-list-get-bullet list-beg struct))
- (bullet-rule-p (cdr (assq 'bullet org-list-automatic-rules)))
- (alpha-p (org-list-use-alpha-bul-p list-beg struct prevs))
- (case-fold-search nil)
- (current (cond
- ((string-match "[a-z]\\." bullet) "a.")
- ((string-match "[a-z])" bullet) "a)")
- ((string-match "[A-Z]\\." bullet) "A.")
- ((string-match "[A-Z])" bullet) "A)")
- ((string-match "\\." bullet) "1.")
- ((string-match ")" bullet) "1)")
- (t (org-trim bullet))))
- ;; Compute list of possible bullets, depending on context
- (bullet-list
- (append '("-" "+" )
- ;; *-bullets are not allowed at column 0
- (unless (and bullet-rule-p
- (looking-at "\\S-")) '("*"))
- ;; Description items cannot be numbered
- (unless (or (eq org-plain-list-ordered-item-terminator ?\))
- (and bullet-rule-p (org-at-item-description-p)))
- '("1."))
- (unless (or (eq org-plain-list-ordered-item-terminator ?.)
- (and bullet-rule-p (org-at-item-description-p)))
- '("1)"))
- (unless (or (not alpha-p)
- (eq org-plain-list-ordered-item-terminator ?\))
- (and bullet-rule-p (org-at-item-description-p)))
- '("a." "A."))
- (unless (or (not alpha-p)
- (eq org-plain-list-ordered-item-terminator ?.)
- (and bullet-rule-p (org-at-item-description-p)))
- '("a)" "A)"))))
- (len (length bullet-list))
- (item-index (- len (length (member current bullet-list))))
- (get-value (lambda (index) (nth (mod index len) bullet-list)))
- (new (cond
- ((member which bullet-list) which)
- ((numberp which) (funcall get-value which))
- ((eq 'previous which) (funcall get-value (1- item-index)))
- (t (funcall get-value (1+ item-index))))))
- ;; Use a short variation of `org-list-write-struct' as there's
- ;; no need to go through all the steps.
- (let ((old-struct (copy-tree struct)))
- (org-list-set-bullet list-beg struct (org-list-bullet-string new))
- (org-list-struct-fix-bul struct prevs)
- (org-list-struct-fix-ind struct parents)
- (org-list-struct-apply-struct struct old-struct)))))
- (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
- first checkbox there, and make every item inside 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, taking as reference the first item
- in subtree, ignoring drawers."
- (interactive "P")
- (save-excursion
- (let* (singlep
- block-item
- lim-up
- lim-down
- (orderedp (ignore-errors (org-entry-get nil "ORDERED")))
- (bounds
- ;; In a region, start at first item in region
- (cond
- ((org-region-active-p)
- (let ((limit (region-end)))
- (goto-char (region-beginning))
- (if (org-list-search-forward (org-item-beginning-re) limit t)
- (setq lim-up (point-at-bol))
- (error "No item in region"))
- (setq lim-down (copy-marker limit))))
- ((org-on-heading-p)
- ;; On an heading, start at first item after drawers
- (let ((limit (save-excursion (outline-next-heading) (point))))
- (forward-line 1)
- (when (looking-at org-drawer-regexp)
- (re-search-forward "^[ \t]*:END:" limit nil))
- (if (org-list-search-forward (org-item-beginning-re) limit t)
- (setq lim-up (point-at-bol))
- (error "No item in subtree"))
- (setq lim-down (copy-marker limit))))
- ;; Just one item: set singlep flag
- ((org-at-item-p)
- (setq singlep t)
- (setq lim-up (point-at-bol)
- lim-down (point-at-eol)))
- (t (error "Not at an item or heading, and no active region"))))
- ;; Determine the checkbox going to be applied to all items
- ;; within bounds
- (ref-checkbox
- (progn
- (goto-char lim-up)
- (let ((cbox (and (org-at-item-checkbox-p) (match-string 1))))
- (cond
- ((equal toggle-presence '(16)) "[-]")
- ((equal toggle-presence '(4))
- (unless cbox "[ ]"))
- ((equal "[ ]" cbox) "[X]")
- (t "[ ]"))))))
- ;; When an item is found within bounds, grab the full list at
- ;; point structure, then: 1. set checkbox of all its items
- ;; within bounds to ref-checkbox; 2. fix checkboxes of the whole
- ;; list; 3. move point after the list.
- (goto-char lim-up)
- (while (and (< (point) lim-down)
- (org-list-search-forward (org-item-beginning-re)
- lim-down 'move))
- (let* ((struct (org-list-struct))
- (struct-copy (copy-tree struct))
- (parents (org-list-parents-alist struct))
- (prevs (org-list-prevs-alist struct))
- (bottom (copy-marker (org-list-get-bottom-point struct)))
- (items-to-toggle (org-remove-if
- (lambda (e) (or (< e lim-up) (> e lim-down)))
- (mapcar 'car (cdr struct)))))
- (mapc (lambda (e) (org-list-set-checkbox
- e struct
- ;; if there is no box at item, leave as-is
- ;; unless function was called with C-u prefix
- (let ((cur-box (org-list-get-checkbox e struct)))
- (if (or cur-box (equal toggle-presence '(4)))
- ref-checkbox
- cur-box))))
- items-to-toggle)
- (setq block-item (org-list-struct-fix-box
- struct parents prevs orderedp))
- ;; Report some problems due to ORDERED status of subtree. If
- ;; only one box was being checked, throw an error, else,
- ;; only signal problems.
- (cond
- ((and singlep block-item (> lim-up block-item))
- (error
- "Checkbox blocked because of unchecked box at line %d"
- (org-current-line block-item)))
- (block-item
- (message
- "Checkboxes were removed due to unchecked box at line %d"
- (org-current-line block-item))))
- (goto-char bottom)
- (org-list-struct-apply-struct struct struct-copy))))
- (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 nil 1))
- (beginning-of-line 2))))
- (org-update-checkbox-count-maybe)))
- (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 ((cookie-re "\\(\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*/[0-9]*\\]\\)\\)")
- (box-re "^[ \t]*\\([-+*]\\|\\([0-9]+\\|[A-Za-z]\\)[.)]\\)[ \t]+\\(?:\\[@\\(?:start:\\)?\\([0-9]+\\|[A-Za-z]\\)\\][ \t]*\\)?\\(\\[[- X]\\]\\)")
- (recursivep
- (or (not org-hierarchical-checkbox-statistics)
- (string-match "\\<recursive\\>"
- (or (ignore-errors
- (org-entry-get nil "COOKIE_DATA"))
- ""))))
- (bounds (if all
- (cons (point-min) (point-max))
- (cons (or (ignore-errors (org-back-to-heading) (point))
- (point-min))
- (save-excursion (outline-next-heading) (point)))))
- (count-boxes
- (function
- ;; Return number of checked boxes and boxes of all types
- ;; in all structures in STRUCTS. If RECURSIVEP is non-nil,
- ;; also count boxes in sub-lists. If ITEM is nil, count
- ;; across the whole structure, else count only across
- ;; subtree whose ancestor is ITEM.
- (lambda (item structs recursivep)
- (let ((c-on 0) (c-all 0))
- (mapc
- (lambda (s)
- (let* ((pre (org-list-prevs-alist s))
- (par (org-list-parents-alist s))
- (items
- (cond
- ((and recursivep item) (org-list-get-subtree item s))
- (recursivep (mapcar 'car s))
- (item (org-list-get-children item s par))
- (t (org-list-get-all-items
- (org-list-get-top-point s) s pre))))
- (cookies (delq nil (mapcar
- (lambda (e)
- (org-list-get-checkbox e s))
- items))))
- (setq c-all (+ (length cookies) c-all)
- c-on (+ (org-count "[X]" cookies) c-on))))
- structs)
- (cons c-on c-all)))))
- (backup-end 1)
- cookies-list structs-bak box-num)
- (goto-char (car bounds))
- ;; 1. Build an alist for each cookie found within BOUNDS. The
- ;; key will be position at beginning of cookie and values
- ;; ending position, format of cookie, and a cell whose car is
- ;; number of checked boxes to report, and cdr total number of
- ;; boxes.
- (while (re-search-forward cookie-re (cdr bounds) t)
- (catch 'skip
- (save-excursion
- (push
- (list
- (match-beginning 1) ; cookie start
- (match-end 1) ; cookie end
- (match-string 2) ; percent?
- (cond ; boxes count
- ;; Cookie is at an heading, but specifically for todo,
- ;; not for checkboxes: skip it.
- ((and (org-on-heading-p)
- (string-match "\\<todo\\>"
- (downcase
- (or (org-entry-get nil "COOKIE_DATA") ""))))
- (throw 'skip nil))
- ;; Cookie is at an heading, but all lists before next
- ;; heading already have been read. Use data collected
- ;; in STRUCTS-BAK. This should only happen when heading
- ;; has more than one cookie on it.
- ((and (org-on-heading-p)
- (<= (save-excursion (outline-next-heading) (point))
- backup-end))
- (funcall count-boxes nil structs-bak recursivep))
- ;; Cookie is at a fresh heading. Grab structure of
- ;; every list containing a checkbox between point and
- ;; next headline, and save them in STRUCTS-BAK.
- ((org-on-heading-p)
- (setq backup-end (save-excursion
- (outline-next-heading) (point)))
- (while (org-list-search-forward box-re backup-end 'move)
- (let* ((struct (org-list-struct))
- (bottom (org-list-get-bottom-point struct)))
- (push struct structs-bak)
- (goto-char bottom)))
- (funcall count-boxes nil structs-bak recursivep))
- ;; Cookie is at an item, and we already have list
- ;; structure stored in STRUCTS-BAK.
- ((and (org-at-item-p)
- (< (point-at-bol) backup-end)
- ;; Only lists in no special context are stored.
- (not (nth 2 (org-list-context))))
- (funcall count-boxes (point-at-bol) structs-bak recursivep))
- ;; Cookie is at an item, but we need to compute list
- ;; structure.
- ((org-at-item-p)
- (let ((struct (org-list-struct)))
- (setq backup-end (org-list-get-bottom-point struct)
- structs-bak (list struct)))
- (funcall count-boxes (point-at-bol) structs-bak recursivep))
- ;; Else, cookie found is at a wrong place. Skip it.
- (t (throw 'skip nil))))
- cookies-list))))
- ;; 2. Apply alist to buffer, in reverse order so positions stay
- ;; unchanged after cookie modifications.
- (mapc (lambda (cookie)
- (let* ((beg (car cookie))
- (end (nth 1 cookie))
- (percentp (nth 2 cookie))
- (checked (car (nth 3 cookie)))
- (total (cdr (nth 3 cookie)))
- (new (if percentp
- (format "[%d%%]" (/ (* 100 checked)
- (max 1 total)))
- (format "[%d/%d]" checked total))))
- (goto-char beg)
- (insert new)
- (delete-region (point) (+ (point) (- end beg)))))
- cookies-list))))
- (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)))
- (defun org-update-checkbox-count-maybe ()
- "Update checkbox statistics unless turned off by user."
- (when (cdr (assq 'checkbox org-list-automatic-rules))
- (org-update-checkbox-count))
- (run-hooks 'org-checkbox-statistics-hook))
- (defvar org-last-indent-begin-marker (make-marker))
- (defvar org-last-indent-end-marker (make-marker))
- (defun org-list-indent-item-generic (arg no-subtree struct)
- "Indent a local list item including its children.
- When number ARG is a negative, item will be outdented, otherwise
- it will be indented.
- If a region is active, all items inside will be moved.
- If NO-SUBTREE is non-nil, only indent the item itself, not its
- children.
- STRUCT is the list structure. Return t if successful."
- (save-excursion
- (beginning-of-line)
- (let* ((regionp (org-region-active-p))
- (rbeg (and regionp (region-beginning)))
- (rend (and regionp (region-end)))
- (top (org-list-get-top-point struct))
- (parents (org-list-parents-alist struct))
- (prevs (org-list-prevs-alist struct))
- ;; Are we going to move the whole list?
- (specialp
- (and (= top (point))
- (cdr (assq 'indent org-list-automatic-rules))
- (if no-subtree
- (error
- "First item of list cannot move without its subtree")
- t))))
- ;; Determine begin and end points of zone to indent. If moving
- ;; more than one item, save them for subsequent moves.
- (unless (and (memq last-command '(org-shiftmetaright org-shiftmetaleft))
- (memq this-command '(org-shiftmetaright org-shiftmetaleft)))
- (if regionp
- (progn
- (set-marker org-last-indent-begin-marker rbeg)
- (set-marker org-last-indent-end-marker rend))
- (set-marker org-last-indent-begin-marker (point))
- (set-marker org-last-indent-end-marker
- (cond
- (specialp (org-list-get-bottom-point struct))
- (no-subtree (1+ (point)))
- (t (org-list-get-item-end (point) struct))))))
- (let* ((beg (marker-position org-last-indent-begin-marker))
- (end (marker-position org-last-indent-end-marker)))
- (cond
- ;; Special case: moving top-item with indent rule
- (specialp
- (let* ((level-skip (org-level-increment))
- (offset (if (< arg 0) (- level-skip) level-skip))
- (top-ind (org-list-get-ind beg struct))
- (old-struct (copy-tree struct)))
- (if (< (+ top-ind offset) 0)
- (error "Cannot outdent beyond margin")
- ;; Change bullet if necessary
- (when (and (= (+ top-ind offset) 0)
- (string-match "*"
- (org-list-get-bullet beg struct)))
- (org-list-set-bullet beg struct
- (org-list-bullet-string "-")))
- ;; Shift every item by OFFSET and fix bullets. Then
- ;; apply changes to buffer.
- (mapc (lambda (e)
- (let ((ind (org-list-get-ind (car e) struct)))
- (org-list-set-ind (car e) struct (+ ind offset))))
- struct)
- (org-list-struct-fix-bul struct prevs)
- (org-list-struct-apply-struct struct old-struct))))
- ;; Forbidden move:
- ((and (< arg 0)
- ;; If only one item is moved, it mustn't have a child
- (or (and no-subtree
- (not regionp)
- (org-list-has-child-p beg struct))
- ;; If a subtree or region is moved, the last item
- ;; of the subtree mustn't have a child
- (let ((last-item (caar
- (reverse
- (org-remove-if
- (lambda (e) (>= (car e) end))
- struct)))))
- (org-list-has-child-p last-item struct))))
- (error "Cannot outdent an item without its children"))
- ;; Normal shifting
- (t
- (let* ((new-parents
- (if (< arg 0)
- (org-list-struct-outdent beg end struct parents)
- (org-list-struct-indent beg end struct parents prevs))))
- (org-list-write-struct struct new-parents))
- (org-update-checkbox-count-maybe))))))
- t)
- (defun org-outdent-item ()
- "Outdent a local list item, but not its children.
- If a region is active, all items inside will be moved."
- (interactive)
- (if (org-at-item-p)
- (let ((struct (org-list-struct)))
- (org-list-indent-item-generic -1 t struct))
- (error "Not at an item")))
- (defun org-indent-item ()
- "Indent a local list item, but not its children.
- If a region is active, all items inside will be moved."
- (interactive)
- (if (org-at-item-p)
- (let ((struct (org-list-struct)))
- (org-list-indent-item-generic 1 t struct))
- (error "Not at an item")))
- (defun org-outdent-item-tree ()
- "Outdent a local list item including its children.
- If a region is active, all items inside will be moved."
- (interactive)
- (let ((regionp (org-region-active-p)))
- (cond
- ((or (org-at-item-p)
- (and (org-region-active-p)
- (goto-char (region-beginning))
- (org-at-item-p)))
- (let ((struct (org-list-struct)))
- (org-list-indent-item-generic -1 nil struct)))
- (regionp (error "Region not starting at an item"))
- (t (error "Not at an item")))))
- (defun org-indent-item-tree ()
- "Indent a local list item including its children.
- If a region is active, all items inside will be moved."
- (interactive)
- (let ((regionp (org-region-active-p)))
- (cond
- ((or (org-at-item-p)
- (and (org-region-active-p)
- (goto-char (region-beginning))
- (org-at-item-p)))
- (let ((struct (org-list-struct)))
- (org-list-indent-item-generic 1 nil struct)))
- (regionp (error "Region not starting at an item"))
- (t (error "Not at an item")))))
- (defvar org-tab-ind-state)
- (defun org-cycle-item-indentation ()
- "Cycle levels of indentation of an empty item.
- The first run indents the item, if applicable. Subsequents runs
- outdent it at meaningful levels in the list. When done, item is
- put back at its original position with its original bullet.
- Return t at each successful move."
- (when (org-at-item-p)
- (let* ((org-adapt-indentation nil)
- (struct (org-list-struct))
- (ind (org-list-get-ind (point-at-bol) struct))
- (bullet (org-list-get-bullet (point-at-bol) struct)))
- ;; Check that item is really empty
- (when (and (save-excursion
- (beginning-of-line)
- (looking-at org-list-full-item-re))
- (>= (match-end 0) (save-excursion
- (goto-char (org-list-get-item-end
- (point-at-bol) struct))
- (skip-chars-backward " \r\t\n")
- (point))))
- (setq this-command 'org-cycle-item-indentation)
- ;; When in the middle of the cycle, try to outdent first. If it
- ;; fails, and point is still at initial position, indent. Else,
- ;; go back to original position.
- (if (eq last-command 'org-cycle-item-indentation)
- (cond
- ((ignore-errors (org-list-indent-item-generic -1 t struct)))
- ((and (= ind (car org-tab-ind-state))
- (ignore-errors (org-list-indent-item-generic 1 t struct))))
- (t (back-to-indentation)
- (org-indent-to-column (car org-tab-ind-state))
- (looking-at "\\S-+")
- (replace-match (cdr org-tab-ind-state))
- (end-of-line)
- ;; Break cycle
- (setq this-command 'identity)))
- ;; If a cycle is starting, remember indentation and bullet,
- ;; then try to indent. If it fails, try to outdent.
- (setq org-tab-ind-state (cons ind bullet))
- (cond
- ((ignore-errors (org-list-indent-item-generic 1 t struct)))
- ((ignore-errors (org-list-indent-item-generic -1 t struct)))
- (t (error "Cannot move item"))))
- t))))
- (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.
- Comparing entries ignores case by default. However, with an
- optional argument WITH-CASE, the sorting considers case as well.
- 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. Only the first line of item is checked.
- 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, sort the timers.
- 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. It will then use COMPARE-FUNC to
- compare entries."
- (interactive "P")
- (let* ((case-func (if with-case 'identity 'downcase))
- (struct (org-list-struct))
- (prevs (org-list-prevs-alist struct))
- (start (org-list-get-list-begin (point-at-bol) struct prevs))
- (end (org-list-get-list-end (point-at-bol) struct prevs))
- (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)
- (goto-char (point-min))
- (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)))
- (next-record (lambda ()
- (skip-chars-forward " \r\t\n")
- (beginning-of-line)))
- (end-record (lambda ()
- (goto-char (org-list-get-item-end-before-blank
- (point) struct))))
- (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 (re-search-forward org-ts-regexp (point-at-eol) t)
- (re-search-forward 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)
- next-record
- end-record
- value-to-sort
- nil
- sort-func)
- ;; Read and fix list again, as `sort-subr' probably destroyed
- ;; its structure.
- (org-list-repair)
- (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 whose car is a symbol of list type, among
- `ordered', `unordered' and `descriptive'. Then, each item is a
- list whose car is counter, and cdr are strings and other
- sub-lists. Inside strings, checkboxes are replaced by \"[CBON]\"
- and \"[CBOFF]\".
- For example, the following list:
- 1. first item
- + sub-item one
- + [X] sub-item two
- more text in first item
- 2. [@3] last item
- will be parsed as:
- \(ordered
- \(nil \"first item\"
- \(unordered
- \(nil \"sub-item one\"\)
- \(nil \"[CBON] sub-item two\"\)\)
- \"more text in first item\"\)
- \(3 \"last item\"\)\)
- Point is left at list end."
- (let* ((struct (org-list-struct))
- (prevs (org-list-prevs-alist struct))
- (parents (org-list-parents-alist struct))
- (top (org-list-get-top-point struct))
- (bottom (org-list-get-bottom-point struct))
- out
- parse-item ; for byte-compiler
- (get-text
- (function
- ;; Return text between BEG and END, trimmed, with
- ;; checkboxes replaced.
- (lambda (beg end)
- (let ((text (org-trim (buffer-substring beg end))))
- (if (string-match "\\`\\[\\([X ]\\)\\]" text)
- (replace-match
- (if (equal (match-string 1 text) " ") "CBOFF" "CBON")
- t nil text 1)
- text)))))
- (parse-sublist
- (function
- ;; Return a list whose car is list type and cdr a list of
- ;; items' body.
- (lambda (e)
- (cons (org-list-get-list-type (car e) struct prevs)
- (mapcar parse-item e)))))
- (parse-item
- (function
- ;; Return a list containing counter of item, if any, text
- ;; and any sublist inside it.
- (lambda (e)
- (let ((start (save-excursion
- (goto-char e)
- (looking-at "[ \t]*\\S-+[ \t]+\\(\\[@[:[:alnum:]]+\\][ \t]*\\)?")
- (match-end 0)))
- ;; Get counter number. For alphabetic counter, get
- ;; its position in the alphabet.
- (counter (let ((c (org-list-get-counter e struct)))
- (cond
- ((not c) nil)
- ((string-match "[A-Za-z]" c)
- (- (string-to-char (upcase (match-string 0 c)))
- 64))
- ((string-match "[0-9]+" c)
- (string-to-number (match-string 0 c))))))
- (childp (org-list-has-child-p e struct))
- (end (org-list-get-item-end e struct)))
- ;; If item has a child, store text between bullet and
- ;; next child, then recursively parse all sublists. At
- ;; the end of each sublist, check for the presence of
- ;; text belonging to the original item.
- (if childp
- (let* ((children (org-list-get-children e struct parents))
- (body (list (funcall get-text start childp))))
- (while children
- (let* ((first (car children))
- (sub (org-list-get-all-items first struct prevs))
- (last-c (car (last sub)))
- (last-end (org-list-get-item-end last-c struct)))
- (push (funcall parse-sublist sub) body)
- ;; Remove children from the list just parsed.
- (setq children (cdr (member last-c children)))
- ;; There is a chunk of text belonging to the
- ;; item if last child doesn't end where next
- ;; child starts or where item ends.
- (unless (= (or (car children) end) last-end)
- (push (funcall get-text
- last-end (or (car children) end))
- body))))
- (cons counter (nreverse body)))
- (list counter (funcall get-text start end))))))))
- ;; Store output, take care of cursor position and deletion of
- ;; list, then return output.
- (setq out (funcall parse-sublist (org-list-get-all-items top struct prevs)))
- (goto-char top)
- (when delete
- (delete-region top bottom)
- (when (and (not (eq org-list-ending-method 'indent))
- (looking-at org-list-end-re))
- (replace-match "\n")))
- out))
- (defun org-list-make-subtree ()
- "Convert the plain list at point into a subtree."
- (interactive)
- (if (not (ignore-errors (goto-char (org-in-item-p))))
- (error "Not in a list")
- (let ((list (save-excursion (org-list-parse-list t))))
- (insert (org-list-to-subtree 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 item"))
- (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-beginning-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"))
- (delete-region beg (point-at-bol))
- (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.
- :icount String to start an item with a counter.
- :iend String to end a list item
- :isep String to separate items
- :lsep String to separate sublists
- :csep String to separate text from a sub-list
- :cboff String to insert for an unchecked checkbox
- :cbon String to insert for a checked checkbox
- Alternatively, each parameter can also be a form returning a
- string. These sexp can use keywords `counter' and `depth',
- reprensenting respectively counter associated to the current
- item, and depth of the current sub-list, starting at 0.
- Obviously, `counter' is only available for parameters applying to
- items."
- (interactive)
- (let* ((p params)
- (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))
- (icount (plist-get p :icount))
- (iend (plist-get p :iend))
- (isep (plist-get p :isep))
- (lsep (plist-get p :lsep))
- (csep (plist-get p :csep))
- (cbon (plist-get p :cbon))
- (cboff (plist-get p :cboff))
- export-sublist ; for byte-compiler
- (export-item
- (function
- ;; Export an item ITEM of type TYPE, at DEPTH. First string
- ;; in item is treated in a special way as it can bring
- ;; extra information that needs to be processed.
- (lambda (item type depth)
- (let* ((counter (pop item))
- (fmt (concat (cond
- ((eq type 'descriptive)
- (concat (org-trim (eval istart)) "%s"
- (eval ddend)))
- ((and counter (eq type 'ordered))
- (concat (eval icount) "%s"))
- (t (concat (eval istart) "%s")))
- (eval iend)))
- (first (car item)))
- ;; Replace checkbox if any is found.
- (cond
- ((string-match "\\[CBON\\]" first)
- (setq first (replace-match cbon t t first)))
- ((string-match "\\[CBOFF\\]" first)
- (setq first (replace-match cboff t t first)))
- ((string-match "\\[-\\]" first)
- (setq first (replace-match "$\\boxminus$" t t first))))
- ;; Insert descriptive term if TYPE is `descriptive'.
- (when (and (eq type 'descriptive)
- (string-match "^\\(.*\\)[ \t]+::" first))
- (setq first (concat
- (eval dtstart) (org-trim (match-string 1 first))
- (eval dtend) (eval ddstart)
- (org-trim (substring first (match-end 0))) "")))
- (setcar item first)
- (format fmt
- (mapconcat (lambda (e)
- (if (stringp e) e
- (funcall export-sublist e (1+ depth))))
- item (or (eval csep) "")))))))
- (export-sublist
- (function
- ;; Export sublist SUB at DEPTH
- (lambda (sub depth)
- (let* ((type (car sub))
- (items (cdr sub))
- (fmt (concat (cond
- (splicep "%s")
- ((eq type 'ordered)
- (concat (eval ostart) "\n%s" (eval oend)))
- ((eq type 'descriptive)
- (concat (eval dstart) "\n%s" (eval dend)))
- (t (concat (eval ustart) "\n%s" (eval uend))))
- (eval lsep))))
- (format fmt (mapconcat (lambda (e)
- (funcall export-item e type depth))
- items (or (eval isep) ""))))))))
- (concat (funcall export-sublist list 0) "\n")))
- (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
- '(:splice nil :ostart "\\begin{enumerate}" :oend "\\end{enumerate}"
- :ustart "\\begin{itemize}" :uend "\\end{itemize}"
- :dstart "\\begin{description}" :dend "\\end{description}"
- :dtstart "[" :dtend "] "
- :istart "\\item " :iend "\n"
- :icount (let ((enum (nth depth '("i" "ii" "iii" "iv"))))
- (if enum
- (format "\\setcounter{enum%s}{%s}\n\\item "
- enum counter)
- "\\item "))
- :csep "\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
- '(:splice nil :ostart "<ol>" :oend "\n</ol>"
- :ustart "<ul>" :uend "\n</ul>"
- :dstart "<dl>" :dend "</dl>"
- :dtstart "<dt>" :dtend "</dt>\n"
- :ddstart "<dd>" :ddend "</dd>"
- :istart "<li>" :iend "</li>"
- :icount (format "<li value=\"%s\">" counter)
- :isep "\n" :lsep "\n" :csep "\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
- '(:splice nil :ostart "@itemize @minus" :oend "@end itemize"
- :ustart "@enumerate" :uend "@end enumerate"
- :dstart "@table @asis" :dend "@end table"
- :dtstart " " :dtend "\n"
- :istart "@item\n" :iend "\n"
- :icount "@item\n"
- :csep "\n"
- :cbon "@code{[X]}" :cboff "@code{[ ]}")
- params)))
- (defun org-list-to-subtree (list &optional params)
- "Convert LIST into an Org subtree.
- LIST is as returned by `org-list-parse-list'. PARAMS is a property list
- with overruling parameters for `org-list-to-generic'."
- (let* ((rule (cdr (assq 'heading org-blank-before-new-entry)))
- (level (or (org-current-level) 0))
- (blankp (or (eq rule t)
- (and (eq rule 'auto)
- (save-excursion
- (outline-previous-heading)
- (org-previous-line-empty-p)))))
- (get-stars
- (function
- ;; Return the string for the heading, depending on depth D
- ;; of current sub-list.
- (lambda (d)
- (concat
- (make-string (+ level
- (if org-odd-levels-only (* 2 (1+ d)) (1+ d)))
- ?*)
- " ")))))
- (org-list-to-generic
- list
- (org-combine-plists
- '(:splice t
- :dtstart " " :dtend " "
- :istart (funcall get-stars depth)
- :icount (funcall get-stars depth)
- :isep (if blankp "\n\n" "\n")
- :csep (if blankp "\n\n" "\n")
- :cbon "DONE" :cboff "TODO")
- params))))
- (provide 'org-list)
- ;; arch-tag: 73cf50c1-200f-4d1d-8a53-4e842a5b11c8
- ;;; org-list.el ends here
|