org-fold.el 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. ;;; org-fold.el --- Folding of Org entries -*- lexical-binding: t; -*-
  2. ;;
  3. ;; Copyright (C) 2020-2020 Free Software Foundation, Inc.
  4. ;;
  5. ;; Author: Ihor Radchenko <yantar92 at gmail dot com>
  6. ;; Keywords: folding, invisible text
  7. ;; URL: https://orgmode.org
  8. ;;
  9. ;; This file is part of GNU Emacs.
  10. ;;
  11. ;; GNU Emacs is free software: you can redistribute it and/or modify
  12. ;; it under the terms of the GNU General Public License as published by
  13. ;; the Free Software Foundation, either version 3 of the License, or
  14. ;; (at your option) any later version.
  15. ;; GNU Emacs is distributed in the hope that it will be useful,
  16. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ;; GNU General Public License for more details.
  19. ;; You should have received a copy of the GNU General Public License
  20. ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
  21. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  22. ;;
  23. ;;; Commentary:
  24. ;; This file contains code handling temporary invisibility (folding
  25. ;; and unfolding) of text in org buffers.
  26. ;; The folding is implemented using generic org-fold-core library. This file
  27. ;; contains org-specific implementation of the folding. Also, various
  28. ;; useful functions from org-fold-core are aliased under shorted `org-fold'
  29. ;; prefix.
  30. ;; The following features are implemented:
  31. ;; - Folding/unfolding various Org mode elements and regions of Org buffers:
  32. ;; + Region before first heading;
  33. ;; + Org headings, their text, children (subtree), siblings, parents, etc;
  34. ;; + Org blocks and drawers
  35. ;; - Revealing Org structure around invisible point location
  36. ;; - Revealing folded Org elements broken by user edits
  37. ;;; Code:
  38. (require 'org-macs)
  39. (require 'org-fold-core)
  40. (defvar org-inlinetask-min-level)
  41. (defvar org-link--link-folding-spec)
  42. (defvar org-link--description-folding-spec)
  43. (defvar org-odd-levels-only)
  44. (defvar org-drawer-regexp)
  45. (defvar org-property-end-re)
  46. (defvar org-link-descriptive)
  47. (defvar org-outline-regexp-bol)
  48. (defvar org-archive-tag)
  49. (defvar org-custom-properties-overlays)
  50. (defvar org-element-headline-re)
  51. (declare-function isearch-filter-visible "isearch" (beg end))
  52. (declare-function org-element-type "org-element" (element))
  53. (declare-function org-element-at-point "org-element" (&optional pom cached-only))
  54. (declare-function org-element-property "org-element" (property element))
  55. (declare-function org-element--current-element "org-element" (limit &optional granularity mode structure))
  56. (declare-function org-element--cache-active-p "org-element" ())
  57. (declare-function org-toggle-custom-properties-visibility "org" ())
  58. (declare-function org-item-re "org-list" ())
  59. (declare-function org-up-heading-safe "org" ())
  60. (declare-function org-get-tags "org" (&optional pos local fontify))
  61. (declare-function org-get-valid-level "org" (level &optional change))
  62. (declare-function org-before-first-heading-p "org" ())
  63. (declare-function org-goto-sibling "org" (&optional previous))
  64. (declare-function org-block-map "org" (function &optional start end))
  65. (declare-function org-map-region "org" (fun beg end))
  66. (declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
  67. (declare-function org-back-to-heading-or-point-min "org" (&optional invisible-ok))
  68. (declare-function org-back-to-heading "org" (&optional invisible-ok))
  69. (declare-function org-at-heading-p "org" (&optional invisible-not-ok))
  70. (declare-function org-cycle-hide-drawers "org-cycle" (state))
  71. (declare-function outline-show-branches "outline" ())
  72. (declare-function outline-hide-sublevels "outline" (levels))
  73. (declare-function outline-get-next-sibling "outline" ())
  74. (declare-function outline-invisible-p "outline" (&optional pos))
  75. (declare-function outline-next-heading "outline" ())
  76. ;;; Customization
  77. (defgroup org-fold-reveal-location nil
  78. "Options about how to make context of a location visible."
  79. :tag "Org Reveal Location"
  80. :group 'org-structure)
  81. (defcustom org-fold-show-context-detail '((agenda . local)
  82. (bookmark-jump . lineage)
  83. (isearch . lineage)
  84. (default . ancestors))
  85. "Alist between context and visibility span when revealing a location.
  86. \\<org-mode-map>Some actions may move point into invisible
  87. locations. As a consequence, Org always exposes a neighborhood
  88. around point. How much is shown depends on the initial action,
  89. or context. Valid contexts are
  90. agenda when exposing an entry from the agenda
  91. org-goto when using the command `org-goto' (`\\[org-goto]')
  92. occur-tree when using the command `org-occur' (`\\[org-sparse-tree] /')
  93. tags-tree when constructing a sparse tree based on tags matches
  94. link-search when exposing search matches associated with a link
  95. mark-goto when exposing the jump goal of a mark
  96. bookmark-jump when exposing a bookmark location
  97. isearch when exiting from an incremental search
  98. default default for all contexts not set explicitly
  99. Allowed visibility spans are
  100. minimal show current headline; if point is not on headline,
  101. also show entry
  102. local show current headline, entry and next headline
  103. ancestors show current headline and its direct ancestors; if
  104. point is not on headline, also show entry
  105. ancestors-full show current subtree and its direct ancestors
  106. lineage show current headline, its direct ancestors and all
  107. their children; if point is not on headline, also show
  108. entry and first child
  109. tree show current headline, its direct ancestors and all
  110. their children; if point is not on headline, also show
  111. entry and all children
  112. canonical show current headline, its direct ancestors along with
  113. their entries and children; if point is not located on
  114. the headline, also show current entry and all children
  115. As special cases, a nil or t value means show all contexts in
  116. `minimal' or `canonical' view, respectively.
  117. Some views can make displayed information very compact, but also
  118. make it harder to edit the location of the match. In such
  119. a case, use the command `org-fold-reveal' (`\\[org-fold-reveal]') to show
  120. more context."
  121. :group 'org-fold-reveal-location
  122. :version "26.1"
  123. :package-version '(Org . "9.0")
  124. :type '(choice
  125. (const :tag "Canonical" t)
  126. (const :tag "Minimal" nil)
  127. (repeat :greedy t :tag "Individual contexts"
  128. (cons
  129. (choice :tag "Context"
  130. (const agenda)
  131. (const org-goto)
  132. (const occur-tree)
  133. (const tags-tree)
  134. (const link-search)
  135. (const mark-goto)
  136. (const bookmark-jump)
  137. (const isearch)
  138. (const default))
  139. (choice :tag "Detail level"
  140. (const minimal)
  141. (const local)
  142. (const ancestors)
  143. (const ancestors-full)
  144. (const lineage)
  145. (const tree)
  146. (const canonical))))))
  147. (defvar org-fold-reveal-start-hook nil
  148. "Hook run before revealing a location.")
  149. (defcustom org-fold-catch-invisible-edits 'smart
  150. "Check if in invisible region before inserting or deleting a character.
  151. Valid values are:
  152. nil Do not check, so just do invisible edits.
  153. error Throw an error and do nothing.
  154. show Make point visible, and do the requested edit.
  155. show-and-error Make point visible, then throw an error and abort the edit.
  156. smart Make point visible, and do insertion/deletion if it is
  157. adjacent to visible text and the change feels predictable.
  158. Never delete a previously invisible character or add in the
  159. middle or right after an invisible region. Basically, this
  160. allows insertion and backward-delete right before ellipses.
  161. FIXME: maybe in this case we should not even show?"
  162. :group 'org-edit-structure
  163. :version "24.1"
  164. :type '(choice
  165. (const :tag "Do not check" nil)
  166. (const :tag "Throw error when trying to edit" error)
  167. (const :tag "Unhide, but do not do the edit" show-and-error)
  168. (const :tag "Show invisible part and do the edit" show)
  169. (const :tag "Be smart and do the right thing" smart)))
  170. ;;; Core functionality
  171. ;;; API
  172. ;;;; Modifying folding specs
  173. (defalias 'org-fold-folding-spec-p #'org-fold-core-folding-spec-p)
  174. (defalias 'org-fold-add-folding-spec #'org-fold-core-add-folding-spec)
  175. (defalias 'org-fold-remove-folding-spec #'org-fold-core-remove-folding-spec)
  176. (defun org-fold-initialize (ellipsis)
  177. "Setup folding in current Org buffer."
  178. (setq-local org-fold-core-isearch-open-function #'org-fold--isearch-reveal)
  179. (setq-local org-fold-core-extend-changed-region-functions (list #'org-fold--extend-changed-region))
  180. ;; FIXME: Converting org-link + org-description to overlays when
  181. ;; search matches hidden "[[" part of the link, reverses priority of
  182. ;; link and description and hides the whole link. Working around
  183. ;; this until there will be no need to convert text properties to
  184. ;; overlays for isearch.
  185. (setq-local org-fold-core--isearch-special-specs '(org-link))
  186. (org-fold-core-initialize
  187. `((org-fold-outline
  188. (:ellipsis . ,ellipsis)
  189. (:fragile . ,#'org-fold--reveal-outline-maybe)
  190. (:isearch-open . t)
  191. ;; This is needed to make sure that inserting a
  192. ;; new planning line in folded heading is not
  193. ;; revealed. Also, the below combination of :font-sticky and
  194. ;; :real-sticky conforms to the overlay properties in outline.el
  195. ;; and the older Org versions as in `outline-flag-region'.
  196. (:front-sticky . t)
  197. (:rear-sticky . nil)
  198. (:font-lock-skip . t)
  199. (:alias . (headline heading outline inlinetask plain-list)))
  200. (org-fold-block
  201. (:ellipsis . ,ellipsis)
  202. (:fragile . ,#'org-fold--reveal-drawer-or-block-maybe)
  203. (:isearch-open . t)
  204. (:front-sticky . t)
  205. (:alias . ( block center-block comment-block
  206. dynamic-block example-block export-block
  207. quote-block special-block src-block
  208. verse-block)))
  209. (org-fold-drawer
  210. (:ellipsis . ,ellipsis)
  211. (:fragile . ,#'org-fold--reveal-drawer-or-block-maybe)
  212. (:isearch-open . t)
  213. (:front-sticky . t)
  214. (:alias . (drawer property-drawer)))
  215. ,org-link--description-folding-spec
  216. ,org-link--link-folding-spec)))
  217. ;;;; Searching and examining folded text
  218. (defalias 'org-fold-folded-p #'org-fold-core-folded-p)
  219. (defalias 'org-fold-get-folding-spec #'org-fold-core-get-folding-spec)
  220. (defalias 'org-fold-get-folding-specs-in-region #'org-fold-core-get-folding-specs-in-region)
  221. (defalias 'org-fold-get-region-at-point #'org-fold-core-get-region-at-point)
  222. (defalias 'org-fold-next-visibility-change #'org-fold-core-next-visibility-change)
  223. (defalias 'org-fold-previous-visibility-change #'org-fold-core-previous-visibility-change)
  224. (defalias 'org-fold-next-folding-state-change #'org-fold-core-next-folding-state-change)
  225. (defalias 'org-fold-previous-folding-state-change #'org-fold-core-previous-folding-state-change)
  226. (defalias 'org-fold-search-forward #'org-fold-core-search-forward)
  227. ;;;;; Macros
  228. (defmacro org-fold-save-outline-visibility--overlays (use-markers &rest body)
  229. "Save and restore outline visibility around BODY.
  230. If USE-MARKERS is non-nil, use markers for the positions. This
  231. means that the buffer may change while running BODY, but it also
  232. means that the buffer should stay alive during the operation,
  233. because otherwise all these markers will point to nowhere."
  234. (declare (debug (form body)) (indent 1))
  235. (org-with-gensyms (data invisible-types markers?)
  236. `(let* ((,invisible-types '(org-hide-block outline))
  237. (,markers? ,use-markers)
  238. (,data
  239. (mapcar (lambda (o)
  240. (let ((beg (overlay-start o))
  241. (end (overlay-end o))
  242. (type (overlay-get o 'invisible)))
  243. (and beg end
  244. (> end beg)
  245. (memq type ,invisible-types)
  246. (list (if ,markers? (copy-marker beg) beg)
  247. (if ,markers? (copy-marker end t) end)
  248. type))))
  249. (org-with-wide-buffer
  250. (overlays-in (point-min) (point-max))))))
  251. (unwind-protect (progn ,@body)
  252. (org-with-wide-buffer
  253. (dolist (type ,invisible-types)
  254. (remove-overlays (point-min) (point-max) 'invisible type))
  255. (pcase-dolist (`(,beg ,end ,type) (delq nil ,data))
  256. (org-fold-region beg end t type)
  257. (when ,markers?
  258. (set-marker beg nil)
  259. (set-marker end nil))))))))
  260. (defmacro org-fold-save-outline-visibility--text-properties (use-markers &rest body)
  261. "Save and restore outline visibility around BODY.
  262. If USE-MARKERS is non-nil, use markers for the positions. This
  263. means that the buffer may change while running BODY, but it also
  264. means that the buffer should stay alive during the operation,
  265. because otherwise all these markers will point to nowhere."
  266. (declare (debug (form body)) (indent 1))
  267. (org-with-gensyms (data specs markers?)
  268. `(let* ((,specs (org-fold-core-folding-spec-list))
  269. (,markers? ,use-markers)
  270. (,data
  271. (org-with-wide-buffer
  272. (let (data-val)
  273. (dolist (spec ,specs)
  274. (let ((pos (point-min)))
  275. (while (< pos (point-max))
  276. (when (org-fold-get-folding-spec spec pos)
  277. (let ((region (org-fold-get-region-at-point spec pos)))
  278. (if ,markers?
  279. (push (list (copy-marker (car region))
  280. (copy-marker (cdr region) t)
  281. spec)
  282. data-val)
  283. (push (list (car region) (cdr region) spec)
  284. data-val))))
  285. (setq pos (org-fold-next-folding-state-change spec pos)))))
  286. data-val))))
  287. (unwind-protect (progn ,@body)
  288. (org-with-wide-buffer
  289. (org-fold-region (point-min) (point-max) nil)
  290. (pcase-dolist (`(,beg ,end ,spec) (delq nil ,data))
  291. (org-fold-region beg end t spec)
  292. (when ,markers?
  293. (set-marker beg nil)
  294. (set-marker end nil))))))))
  295. (defmacro org-fold-save-outline-visibility (use-markers &rest body)
  296. "Save and restore outline visibility around BODY.
  297. If USE-MARKERS is non-nil, use markers for the positions. This
  298. means that the buffer may change while running BODY, but it also
  299. means that the buffer should stay alive during the operation,
  300. because otherwise all these markers will point to nowhere."
  301. (declare (debug (form body)) (indent 1))
  302. `(if (eq org-fold-core-style 'text-properties)
  303. (org-fold-save-outline-visibility--text-properties ,use-markers ,@body)
  304. (org-fold-save-outline-visibility--overlays ,use-markers ,@body)))
  305. ;;;; Changing visibility (regions, blocks, drawers, headlines)
  306. ;;;;; Region visibility
  307. ;; (defalias 'org-fold-region #'org-fold-core-region)
  308. (defun org-fold-region--overlays (from to flag spec)
  309. "Hide or show lines from FROM to TO, according to FLAG.
  310. SPEC is the invisibility spec, as a symbol."
  311. (remove-overlays from to 'invisible spec)
  312. ;; Use `front-advance' since text right before to the beginning of
  313. ;; the overlay belongs to the visible line than to the contents.
  314. (when flag
  315. (let ((o (make-overlay from to nil 'front-advance)))
  316. (overlay-put o 'evaporate t)
  317. (overlay-put o 'invisible spec)
  318. (overlay-put o
  319. 'isearch-open-invisible
  320. (lambda (&rest _) (org-fold-show-context 'isearch))))))
  321. (defsubst org-fold-region (from to flag &optional spec)
  322. "Hide or show lines from FROM to TO, according to FLAG.
  323. SPEC is the invisibility spec, as a symbol."
  324. (if (eq org-fold-core-style 'text-properties)
  325. (org-fold-core-region from to flag spec)
  326. (org-fold-region--overlays from to flag spec)))
  327. (defun org-fold-show-all--text-properties (&optional types)
  328. "Show all contents in the visible part of the buffer.
  329. By default, the function expands headings, blocks and drawers.
  330. When optional argument TYPES is a list of symbols among `blocks',
  331. `drawers' and `headings', to only expand one specific type."
  332. (interactive)
  333. (dolist (type (or types '(blocks drawers headings)))
  334. (org-fold-region (point-min) (point-max) nil
  335. (pcase type
  336. (`blocks 'block)
  337. (`drawers 'drawer)
  338. (`headings 'headline)
  339. (_ (error "Invalid type: %S" type))))))
  340. (defun org-fold-show-all--overlays (&optional types)
  341. "Show all contents in the visible part of the buffer.
  342. By default, the function expands headings, blocks and drawers.
  343. When optional argument TYPE is a list of symbols among `blocks',
  344. `drawers' and `headings', to only expand one specific type."
  345. (interactive)
  346. (let ((types (or types '(blocks drawers headings))))
  347. (when (memq 'blocks types)
  348. (org-fold-region (point-min) (point-max) nil 'org-hide-block))
  349. (cond
  350. ;; Fast path. Since headings and drawers share the same
  351. ;; invisible spec, clear everything in one go.
  352. ((and (memq 'headings types)
  353. (memq 'drawers types))
  354. (org-fold-region (point-min) (point-max) nil 'outline))
  355. ((memq 'headings types)
  356. (org-fold-region (point-min) (point-max) nil 'outline)
  357. (org-cycle-hide-drawers 'all))
  358. ((memq 'drawers types)
  359. (save-excursion
  360. (goto-char (point-min))
  361. (while (re-search-forward org-drawer-regexp nil t)
  362. (let* ((pair (get-char-property-and-overlay (line-beginning-position)
  363. 'invisible))
  364. (o (cdr-safe pair)))
  365. (if (overlayp o) (goto-char (overlay-end o))
  366. (pcase (get-char-property-and-overlay (point) 'invisible)
  367. (`(outline . ,o)
  368. (goto-char (overlay-end o))
  369. (delete-overlay o))
  370. (_ nil))))))))))
  371. (defsubst org-fold-show-all (&optional types)
  372. "Show all contents in the visible part of the buffer.
  373. By default, the function expands headings, blocks and drawers.
  374. When optional argument TYPES is a list of symbols among `blocks',
  375. `drawers' and `headings', to only expand one specific type."
  376. (interactive)
  377. (if (eq org-fold-core-style 'text-properties)
  378. (org-fold-show-all--text-properties types)
  379. (org-fold-show-all--overlays types)))
  380. (defun org-fold-flag-above-first-heading (&optional arg)
  381. "Hide from bob up to the first heading.
  382. Move point to the beginning of first heading or end of buffer."
  383. (goto-char (point-min))
  384. (unless (org-at-heading-p)
  385. (outline-next-heading))
  386. (unless (bobp)
  387. (org-fold-region 1 (1- (point)) (not arg) 'outline)))
  388. ;;;;; Heading visibility
  389. (defun org-fold-heading (flag &optional entry)
  390. "Fold/unfold the current heading. FLAG non-nil means make invisible.
  391. When ENTRY is non-nil, show the entire entry."
  392. (save-excursion
  393. (org-back-to-heading t)
  394. ;; Check if we should show the entire entry
  395. (if (not entry)
  396. (org-fold-region
  397. (line-end-position 0) (line-end-position) flag 'outline)
  398. (org-fold-show-entry)
  399. (save-excursion
  400. ;; FIXME: potentially catches inlinetasks
  401. (and (outline-next-heading)
  402. (org-fold-heading nil))))))
  403. (defun org-fold-hide-entry ()
  404. "Hide the body directly following this heading."
  405. (interactive)
  406. (save-excursion
  407. (org-back-to-heading-or-point-min t)
  408. (when (org-at-heading-p) (forward-line))
  409. (unless (eobp) ; Current headline is empty and ends at the end of buffer.
  410. (org-fold-region
  411. (line-end-position 0)
  412. (save-excursion
  413. (if (re-search-forward
  414. (concat "[\r\n]" (org-get-limited-outline-regexp)) nil t)
  415. (line-end-position 0)
  416. (point-max)))
  417. t
  418. 'outline))))
  419. (defun org-fold-subtree (flag)
  420. (save-excursion
  421. (org-back-to-heading t)
  422. (org-fold-region
  423. (line-end-position)
  424. (progn (org-end-of-subtree t) (point))
  425. flag
  426. 'outline)))
  427. ;; Replaces `outline-hide-subtree'.
  428. (defun org-fold-hide-subtree ()
  429. "Hide everything after this heading at deeper levels."
  430. (interactive)
  431. (org-fold-subtree t))
  432. ;; Replaces `outline-hide-sublevels'
  433. (defun org-fold-hide-sublevels (levels)
  434. "Hide everything but the top LEVELS levels of headers, in whole buffer.
  435. This also unhides the top heading-less body, if any.
  436. Interactively, the prefix argument supplies the value of LEVELS.
  437. When invoked without a prefix argument, LEVELS defaults to the level
  438. of the current heading, or to 1 if the current line is not a heading."
  439. (interactive (list
  440. (cond
  441. (current-prefix-arg (prefix-numeric-value current-prefix-arg))
  442. ((save-excursion (beginning-of-line)
  443. (looking-at outline-regexp))
  444. (funcall outline-level))
  445. (t 1))))
  446. (if (< levels 1)
  447. (error "Must keep at least one level of headers"))
  448. (save-excursion
  449. (let* ((beg (progn
  450. (goto-char (point-min))
  451. ;; Skip the prelude, if any.
  452. (unless (org-at-heading-p) (outline-next-heading))
  453. (point)))
  454. (end (progn
  455. (goto-char (point-max))
  456. ;; Keep empty last line, if available.
  457. (max (point-min) (if (bolp) (1- (point)) (point))))))
  458. (if (< end beg)
  459. (setq beg (prog1 end (setq end beg))))
  460. ;; First hide everything.
  461. (org-fold-region beg end t 'headline)
  462. ;; Then unhide the top level headers.
  463. (org-map-region
  464. (lambda ()
  465. (when (<= (funcall outline-level) levels)
  466. (org-fold-heading nil)))
  467. beg end)
  468. ;; Finally unhide any trailing newline.
  469. (goto-char (point-max))
  470. (if (and (bolp) (not (bobp)) (outline-invisible-p (1- (point))))
  471. (org-fold-region (max (point-min) (1- (point))) (point) nil)))))
  472. (defun org-fold-show-entry ()
  473. "Show the body directly following its heading.
  474. Show the heading too, if it is currently invisible."
  475. (interactive)
  476. (save-excursion
  477. (org-back-to-heading-or-point-min t)
  478. (org-fold-region
  479. (line-end-position 0)
  480. (save-excursion
  481. (if (re-search-forward
  482. (concat "[\r\n]\\(" (org-get-limited-outline-regexp) "\\)") nil t)
  483. (match-beginning 1)
  484. (point-max)))
  485. nil
  486. 'outline)
  487. (org-cycle-hide-drawers 'children)))
  488. (defalias 'org-fold-show-hidden-entry #'org-fold-show-entry
  489. "Show an entry where even the heading is hidden.")
  490. (defun org-fold-show-siblings ()
  491. "Show all siblings of the current headline."
  492. (save-excursion
  493. (while (org-goto-sibling) (org-fold-heading nil)))
  494. (save-excursion
  495. (while (org-goto-sibling 'previous)
  496. (org-fold-heading nil))))
  497. (defun org-fold-show-children (&optional level)
  498. "Show all direct subheadings of this heading.
  499. Prefix arg LEVEL is how many levels below the current level
  500. should be shown. Default is enough to cause the following
  501. heading to appear."
  502. (interactive "p")
  503. (unless (org-before-first-heading-p)
  504. (save-excursion
  505. (org-with-limited-levels (org-back-to-heading t))
  506. (let* ((current-level (funcall outline-level))
  507. (max-level (org-get-valid-level
  508. current-level
  509. (if level (prefix-numeric-value level) 1)))
  510. (end (save-excursion (org-end-of-subtree t t)))
  511. (regexp-fmt "^\\*\\{%d,%s\\}\\(?: \\|$\\)")
  512. (past-first-child nil)
  513. ;; Make sure to skip inlinetasks.
  514. (re (format regexp-fmt
  515. current-level
  516. (cond
  517. ((not (featurep 'org-inlinetask)) "")
  518. (org-odd-levels-only (- (* 2 org-inlinetask-min-level)
  519. 3))
  520. (t (1- org-inlinetask-min-level))))))
  521. ;; Display parent heading.
  522. (org-fold-heading nil)
  523. (forward-line)
  524. ;; Display children. First child may be deeper than expected
  525. ;; MAX-LEVEL. Since we want to display it anyway, adjust
  526. ;; MAX-LEVEL accordingly.
  527. (while (re-search-forward re end t)
  528. (unless past-first-child
  529. (setq re (format regexp-fmt
  530. current-level
  531. (max (funcall outline-level) max-level)))
  532. (setq past-first-child t))
  533. (org-fold-heading nil))))))
  534. (defun org-fold-show-subtree ()
  535. "Show everything after this heading at deeper levels."
  536. (interactive)
  537. (org-fold-region
  538. (point) (save-excursion (org-end-of-subtree t t)) nil 'outline))
  539. (defun org-fold-show-branches ()
  540. "Show all subheadings of this heading, but not their bodies."
  541. (interactive)
  542. (org-fold-show-children 1000))
  543. (defun org-fold-show-branches-buffer--text-properties ()
  544. "Show all branches in the buffer."
  545. (org-fold-flag-above-first-heading)
  546. (org-fold-hide-sublevels 1)
  547. (unless (eobp)
  548. (org-fold-show-branches)
  549. (while (outline-get-next-sibling)
  550. (org-fold-show-branches)))
  551. (goto-char (point-min)))
  552. (defun org-fold-show-branches-buffer--overlays ()
  553. "Show all branches in the buffer."
  554. (org-fold-flag-above-first-heading)
  555. (outline-hide-sublevels 1)
  556. (unless (eobp)
  557. (outline-show-branches)
  558. (while (outline-get-next-sibling)
  559. (outline-show-branches)))
  560. (goto-char (point-min)))
  561. (defsubst org-fold-show-branches-buffer ()
  562. "Show all branches in the buffer."
  563. (if (eq org-fold-core-style 'text-properties)
  564. (org-fold-show-branches-buffer--text-properties)
  565. (org-fold-show-branches-buffer--overlays)))
  566. ;;;;; Blocks and drawers visibility
  567. (defun org-fold--hide-wrapper-toggle (element category force no-error)
  568. "Toggle visibility for ELEMENT.
  569. ELEMENT is a block or drawer type parsed element. CATEGORY is
  570. either `block' or `drawer'. When FORCE is `off', show the block
  571. or drawer. If it is non-nil, hide it unconditionally. Throw an
  572. error when not at a block or drawer, unless NO-ERROR is non-nil.
  573. Return a non-nil value when toggling is successful."
  574. (let ((type (org-element-type element)))
  575. (cond
  576. ((memq type
  577. (pcase category
  578. (`drawer '(drawer property-drawer))
  579. (`block '(center-block
  580. comment-block dynamic-block example-block export-block
  581. quote-block special-block src-block verse-block))
  582. (_ (error "Unknown category: %S" category))))
  583. (let* ((post (org-element-property :post-affiliated element))
  584. (start (save-excursion
  585. (goto-char post)
  586. (line-end-position)))
  587. (end (save-excursion
  588. (goto-char (org-element-property :end element))
  589. (skip-chars-backward " \t\n")
  590. (line-end-position))))
  591. ;; Do nothing when not before or at the block opening line or
  592. ;; at the block closing line.
  593. (unless (let ((eol (line-end-position)))
  594. (and (> eol start) (/= eol end)))
  595. (let* ((spec (if (eq org-fold-core-style 'text-properties)
  596. category
  597. (if (eq category 'block) 'org-hide-block 'outline)))
  598. (flag
  599. (cond ((eq force 'off) nil)
  600. (force t)
  601. ((if (eq org-fold-core-style 'text-properties)
  602. (org-fold-folded-p start spec)
  603. (eq spec (get-char-property start 'invisible)))
  604. nil)
  605. (t t))))
  606. (org-fold-region start end flag spec))
  607. ;; When the block is hidden away, make sure point is left in
  608. ;; a visible part of the buffer.
  609. (when (invisible-p (max (1- (point)) (point-min)))
  610. (goto-char post))
  611. ;; Signal success.
  612. t)))
  613. (no-error nil)
  614. (t
  615. (user-error (format "%s@%s: %s"
  616. (buffer-file-name (buffer-base-buffer))
  617. (point)
  618. (if (eq category 'drawer)
  619. "Not at a drawer"
  620. "Not at a block")))))))
  621. (defun org-fold-hide-block-toggle (&optional force no-error element)
  622. "Toggle the visibility of the current block.
  623. When optional argument FORCE is `off', make block visible. If it
  624. is non-nil, hide it unconditionally. Throw an error when not at
  625. a block, unless NO-ERROR is non-nil. When optional argument
  626. ELEMENT is provided, consider it instead of the current block.
  627. Return a non-nil value when toggling is successful."
  628. (interactive)
  629. (org-fold--hide-wrapper-toggle
  630. (or element (org-element-at-point)) 'block force no-error))
  631. (defun org-fold-hide-drawer-toggle (&optional force no-error element)
  632. "Toggle the visibility of the current drawer.
  633. When optional argument FORCE is `off', make drawer visible. If
  634. it is non-nil, hide it unconditionally. Throw an error when not
  635. at a drawer, unless NO-ERROR is non-nil. When optional argument
  636. ELEMENT is provided, consider it instead of the current drawer.
  637. Return a non-nil value when toggling is successful."
  638. (interactive)
  639. (org-fold--hide-wrapper-toggle
  640. (or element (org-element-at-point)) 'drawer force no-error))
  641. (defun org-fold-hide-block-all ()
  642. "Fold all blocks in the current buffer."
  643. (interactive)
  644. (org-block-map (apply-partially #'org-fold-hide-block-toggle 'hide)))
  645. (defun org-fold-hide-drawer-all ()
  646. "Fold all drawers in the current buffer."
  647. (let ((begin (point-min))
  648. (end (point-max)))
  649. (org-fold--hide-drawers begin end)))
  650. (defun org-fold--hide-drawers--overlays (begin end)
  651. "Hide all drawers between BEGIN and END."
  652. (save-excursion
  653. (goto-char begin)
  654. (while (and (< (point) end) (re-search-forward org-drawer-regexp end t))
  655. (let* ((pair (get-char-property-and-overlay (line-beginning-position)
  656. 'invisible))
  657. (o (cdr-safe pair)))
  658. (if (overlayp o) (goto-char (overlay-end o)) ;invisible drawer
  659. (pcase (get-char-property-and-overlay (point) 'invisible)
  660. (`(outline . ,o) (goto-char (overlay-end o))) ;already folded
  661. (_
  662. (let* ((drawer (org-element-at-point))
  663. (type (org-element-type drawer)))
  664. (when (memq type '(drawer property-drawer))
  665. (org-fold-hide-drawer-toggle t nil drawer)
  666. ;; Make sure to skip drawer entirely or we might flag it
  667. ;; another time when matching its ending line with
  668. ;; `org-drawer-regexp'.
  669. (goto-char (org-element-property :end drawer)))))))))))
  670. (defun org-fold--hide-drawers--text-properties (begin end)
  671. "Hide all drawers between BEGIN and END."
  672. (save-excursion
  673. (goto-char begin)
  674. (while (and (< (point) end)
  675. (re-search-forward org-drawer-regexp end t))
  676. ;; Skip folded drawers
  677. (if (org-fold-folded-p nil 'drawer)
  678. (goto-char (org-fold-next-folding-state-change 'drawer nil end))
  679. (let* ((drawer (org-element-at-point))
  680. (type (org-element-type drawer)))
  681. (when (memq type '(drawer property-drawer))
  682. (org-fold-hide-drawer-toggle t nil drawer)
  683. ;; Make sure to skip drawer entirely or we might flag it
  684. ;; another time when matching its ending line with
  685. ;; `org-drawer-regexp'.
  686. (goto-char (org-element-property :end drawer))))))))
  687. (defun org-fold--hide-drawers (begin end)
  688. "Hide all drawers between BEGIN and END."
  689. (if (eq org-fold-core-style 'text-properties)
  690. (org-fold--hide-drawers--text-properties begin end)
  691. (org-fold--hide-drawers--overlays begin end)))
  692. (defun org-fold-hide-archived-subtrees (beg end)
  693. "Re-hide all archived subtrees after a visibility state change."
  694. (org-with-wide-buffer
  695. (let ((case-fold-search nil)
  696. (re (concat org-outline-regexp-bol ".*:" org-archive-tag ":")))
  697. (goto-char beg)
  698. ;; Include headline point is currently on.
  699. (beginning-of-line)
  700. (while (and (< (point) end) (re-search-forward re end t))
  701. (when (member org-archive-tag (org-get-tags nil t))
  702. (org-fold-subtree t)
  703. (org-end-of-subtree t))))))
  704. ;;;;; Reveal point location
  705. (defun org-fold-show-context (&optional key)
  706. "Make sure point and context are visible.
  707. Optional argument KEY, when non-nil, is a symbol. See
  708. `org-fold-show-context-detail' for allowed values and how much is to
  709. be shown."
  710. (org-fold-show-set-visibility
  711. (cond ((symbolp org-fold-show-context-detail) org-fold-show-context-detail)
  712. ((cdr (assq key org-fold-show-context-detail)))
  713. (t (cdr (assq 'default org-fold-show-context-detail))))))
  714. (defun org-fold-show-set-visibility--overlays (detail)
  715. "Set visibility around point according to DETAIL.
  716. DETAIL is either nil, `minimal', `local', `ancestors',
  717. `ancestors-full', `lineage', `tree', `canonical' or t. See
  718. `org-show-context-detail' for more information."
  719. ;; Show current heading and possibly its entry, following headline
  720. ;; or all children.
  721. (if (and (org-at-heading-p) (not (eq detail 'local)))
  722. (org-fold-heading nil)
  723. (org-fold-show-entry)
  724. ;; If point is hidden within a drawer or a block, make sure to
  725. ;; expose it.
  726. (dolist (o (overlays-at (point)))
  727. (when (memq (overlay-get o 'invisible) '(org-hide-block outline))
  728. (delete-overlay o)))
  729. (unless (org-before-first-heading-p)
  730. (org-with-limited-levels
  731. (cl-case detail
  732. ((tree canonical t) (org-fold-show-children))
  733. ((nil minimal ancestors ancestors-full))
  734. (t (save-excursion
  735. (outline-next-heading)
  736. (org-fold-heading nil)))))))
  737. ;; Show whole subtree.
  738. (when (eq detail 'ancestors-full) (org-fold-show-subtree))
  739. ;; Show all siblings.
  740. (when (eq detail 'lineage) (org-fold-show-siblings))
  741. ;; Show ancestors, possibly with their children.
  742. (when (memq detail '(ancestors ancestors-full lineage tree canonical t))
  743. (save-excursion
  744. (while (org-up-heading-safe)
  745. (org-fold-heading nil)
  746. (when (memq detail '(canonical t)) (org-fold-show-entry))
  747. (when (memq detail '(tree canonical t)) (org-fold-show-children))))))
  748. (defvar org-hide-emphasis-markers); Defined in org.el
  749. (defvar org-pretty-entities); Defined in org.el
  750. (defun org-fold-show-set-visibility--text-properties (detail)
  751. "Set visibility around point according to DETAIL.
  752. DETAIL is either nil, `minimal', `local', `ancestors',
  753. `ancestors-full', `lineage', `tree', `canonical' or t. See
  754. `org-show-context-detail' for more information."
  755. ;; Show current heading and possibly its entry, following headline
  756. ;; or all children.
  757. (if (and (org-at-heading-p) (not (eq detail 'local)))
  758. (org-fold-heading nil)
  759. (org-fold-show-entry)
  760. ;; If point is hidden make sure to expose it.
  761. (when (org-invisible-p)
  762. ;; FIXME: No clue why, but otherwise the following might not work.
  763. (redisplay)
  764. (let ((region (org-fold-get-region-at-point)))
  765. ;; Reveal emphasis markers.
  766. (when (eq detail 'local)
  767. (let (org-hide-emphasis-markers
  768. org-link-descriptive
  769. org-pretty-entities
  770. (org-hide-macro-markers nil)
  771. (region (or (org-find-text-property-region (point) 'org-emphasis)
  772. (org-find-text-property-region (point) 'org-macro)
  773. (org-find-text-property-region (point) 'invisible)
  774. region)))
  775. ;; Silence byte-compiler.
  776. (ignore org-hide-macro-markers)
  777. (when region
  778. (org-with-point-at (car region)
  779. (beginning-of-line)
  780. (let (font-lock-extend-region-functions)
  781. (font-lock-fontify-region (max (point-min) (1- (car region))) (cdr region))))))
  782. ;; Unfold links.
  783. (when region
  784. (dolist (spec '(org-link org-link-description))
  785. (org-fold-region (car region) (cdr region) nil spec))))
  786. (when region
  787. (dolist (spec (org-fold-core-folding-spec-list))
  788. ;; Links are taken care by above.
  789. (unless (memq spec '(org-link org-link-description))
  790. (org-fold-region (car region) (cdr region) nil spec))))))
  791. (unless (org-before-first-heading-p)
  792. (org-with-limited-levels
  793. (cl-case detail
  794. ((tree canonical t) (org-fold-show-children))
  795. ((nil minimal ancestors ancestors-full))
  796. (t (save-excursion
  797. (outline-next-heading)
  798. (org-fold-heading nil)))))))
  799. ;; Show whole subtree.
  800. (when (eq detail 'ancestors-full) (org-fold-show-subtree))
  801. ;; Show all siblings.
  802. (when (eq detail 'lineage) (org-fold-show-siblings))
  803. ;; Show ancestors, possibly with their children.
  804. (when (memq detail '(ancestors ancestors-full lineage tree canonical t))
  805. (save-excursion
  806. (while (org-up-heading-safe)
  807. (org-fold-heading nil)
  808. (when (memq detail '(canonical t)) (org-fold-show-entry))
  809. (when (memq detail '(tree canonical t)) (org-fold-show-children))))))
  810. (defun org-fold-show-set-visibility (detail)
  811. "Set visibility around point according to DETAIL.
  812. DETAIL is either nil, `minimal', `local', `ancestors', `lineage',
  813. `tree', `canonical' or t. See `org-fold-show-context-detail' for more
  814. information."
  815. (if (eq org-fold-core-style 'text-properties)
  816. (org-fold-show-set-visibility--text-properties detail)
  817. (org-fold-show-set-visibility--overlays detail)))
  818. (defun org-fold-reveal (&optional siblings)
  819. "Show current entry, hierarchy above it, and the following headline.
  820. This can be used to show a consistent set of context around
  821. locations exposed with `org-fold-show-context'.
  822. With optional argument SIBLINGS, on each level of the hierarchy all
  823. siblings are shown. This repairs the tree structure to what it would
  824. look like when opened with hierarchical calls to `org-cycle'.
  825. With a \\[universal-argument] \\[universal-argument] prefix, \
  826. go to the parent and show the entire tree."
  827. (interactive "P")
  828. (run-hooks 'org-fold-reveal-start-hook)
  829. (cond ((equal siblings '(4)) (org-fold-show-set-visibility 'canonical))
  830. ((equal siblings '(16))
  831. (save-excursion
  832. (when (org-up-heading-safe)
  833. (org-fold-show-subtree)
  834. (run-hook-with-args 'org-cycle-hook 'subtree))))
  835. (t (org-fold-show-set-visibility 'lineage))))
  836. ;;; Make isearch search in some text hidden via text properties.
  837. (defun org-fold--isearch-reveal (&rest _)
  838. "Reveal text at POS found by isearch."
  839. (org-fold-show-context 'isearch))
  840. ;;; Handling changes in folded elements
  841. (defun org-fold--extend-changed-region (from to)
  842. "Consider folded regions in the next/previous line when fixing
  843. region visibility.
  844. This function is intended to be used as a member of
  845. `org-fold-core-extend-changed-region-functions'."
  846. ;; If the edit is done in the first line of a folded drawer/block,
  847. ;; the folded text is only starting from the next line and needs to
  848. ;; be checked.
  849. (setq to (save-excursion (goto-char to) (line-beginning-position 2)))
  850. ;; If the ":END:" line of the drawer is deleted, the folded text is
  851. ;; only ending at the previous line and needs to be checked.
  852. (setq from (save-excursion (goto-char from) (line-beginning-position 0)))
  853. (cons from to))
  854. (defun org-fold--reveal-headline-at-point ()
  855. "Reveal header line and empty contents inside.
  856. Reveal the header line and, if present, also reveal its contents, when
  857. the contents consists of blank lines.
  858. Assume that point is located at the header line."
  859. (org-with-wide-buffer
  860. (beginning-of-line)
  861. (org-fold-region
  862. (max (point-min) (1- (point)))
  863. (let ((endl (line-end-position)))
  864. (save-excursion
  865. (goto-char endl)
  866. (skip-chars-forward "\n\t\r ")
  867. ;; Unfold blank lines after newly inserted headline.
  868. (if (equal (point)
  869. (save-excursion
  870. (goto-char endl)
  871. (org-end-of-subtree)
  872. (skip-chars-forward "\n\t\r ")))
  873. (point)
  874. endl)))
  875. nil 'headline)))
  876. (defun org-fold--reveal-outline-maybe (region _)
  877. "Reveal folded outline in REGION when needed.
  878. This function is intended to be used as :fragile property of
  879. `org-fold-outline' spec. See `org-fold-core--specs' for details."
  880. (save-match-data
  881. (org-with-wide-buffer
  882. (goto-char (car region))
  883. ;; The line before beginning of the fold should be either a
  884. ;; headline or a list item.
  885. (backward-char)
  886. (beginning-of-line)
  887. ;; Make sure that headline is not partially hidden.
  888. (unless (org-fold-folded-p nil 'headline)
  889. (org-fold--reveal-headline-at-point))
  890. ;; Never hide level 1 headlines
  891. (save-excursion
  892. (goto-char (line-end-position))
  893. (unless (>= (point) (cdr region))
  894. (when (re-search-forward (rx bol "* ") (cdr region) t)
  895. (org-fold--reveal-headline-at-point))))
  896. ;; Make sure that headline after is not partially hidden.
  897. (goto-char (cdr region))
  898. (beginning-of-line)
  899. (unless (org-fold-folded-p nil 'headline)
  900. (when (looking-at-p org-element-headline-re)
  901. (org-fold--reveal-headline-at-point)))
  902. ;; Check the validity of headline
  903. (goto-char (car region))
  904. (backward-char)
  905. (beginning-of-line)
  906. (unless (let ((case-fold-search t))
  907. (looking-at (rx-to-string
  908. `(or (regex ,(org-item-re))
  909. (regex ,org-outline-regexp-bol)))))
  910. t))))
  911. (defun org-fold--reveal-drawer-or-block-maybe (region spec)
  912. "Reveal folded drawer/block (according to SPEC) in REGION when needed.
  913. This function is intended to be used as :fragile property of
  914. `org-fold-drawer' or `org-fold-block' spec."
  915. (let ((begin-re (cond
  916. ((eq spec (org-fold-core-get-folding-spec-from-alias 'drawer))
  917. org-drawer-regexp)
  918. ;; Group one below contains the type of the block.
  919. ((eq spec (org-fold-core-get-folding-spec-from-alias 'block))
  920. (rx bol (zero-or-more (any " " "\t"))
  921. "#+begin"
  922. (or ":"
  923. (seq "_"
  924. (group (one-or-more (not (syntax whitespace))))))))))
  925. ;; To be determined later. May depend on `begin-re' match (i.e. for blocks).
  926. end-re)
  927. (save-match-data ; we should not clobber match-data in after-change-functions
  928. (let ((fold-begin (car region))
  929. (fold-end (cdr region)))
  930. (let (unfold?)
  931. (catch :exit
  932. ;; The line before folded text should be beginning of
  933. ;; the drawer/block.
  934. (save-excursion
  935. (goto-char fold-begin)
  936. ;; The line before beginning of the fold should be the
  937. ;; first line of the drawer/block.
  938. (backward-char)
  939. (beginning-of-line)
  940. (unless (let ((case-fold-search t))
  941. (looking-at begin-re)) ; the match-data will be used later
  942. (throw :exit (setq unfold? t))))
  943. ;; Set `end-re' for the current drawer/block.
  944. (setq end-re
  945. (cond
  946. ((eq spec (org-fold-core-get-folding-spec-from-alias 'drawer))
  947. org-property-end-re)
  948. ((eq spec (org-fold-core-get-folding-spec-from-alias 'block))
  949. (let ((block-type (match-string 1))) ; the last match is from `begin-re'
  950. (concat (rx bol (zero-or-more (any " " "\t")) "#+end")
  951. (if block-type
  952. (concat "_"
  953. (regexp-quote block-type)
  954. (rx (zero-or-more (any " " "\t")) eol))
  955. (rx (opt ":") (zero-or-more (any " " "\t")) eol)))))))
  956. ;; The last line of the folded text should match `end-re'.
  957. (save-excursion
  958. (goto-char fold-end)
  959. (beginning-of-line)
  960. (unless (let ((case-fold-search t))
  961. (looking-at end-re))
  962. (throw :exit (setq unfold? t))))
  963. ;; There should be no `end-re' or
  964. ;; `org-outline-regexp-bol' anywhere in the
  965. ;; drawer/block body.
  966. (save-excursion
  967. (goto-char fold-begin)
  968. (when (save-excursion
  969. (let ((case-fold-search t))
  970. (re-search-forward (rx-to-string `(or (regex ,end-re)
  971. (regex ,org-outline-regexp-bol)))
  972. (max (point)
  973. (1- (save-excursion
  974. (goto-char fold-end)
  975. (line-beginning-position))))
  976. t)))
  977. (throw :exit (setq unfold? t)))))
  978. unfold?)))))
  979. ;; Catching user edits inside invisible text
  980. (defun org-fold-check-before-invisible-edit--overlays (kind)
  981. "Check if editing KIND is dangerous with invisible text around.
  982. The detailed reaction depends on the user option
  983. `org-fold-catch-invisible-edits'."
  984. ;; First, try to get out of here as quickly as possible, to reduce overhead
  985. (when (and org-fold-catch-invisible-edits
  986. (or (not (boundp 'visible-mode)) (not visible-mode))
  987. (or (get-char-property (point) 'invisible)
  988. (get-char-property (max (point-min) (1- (point))) 'invisible)))
  989. ;; OK, we need to take a closer look. Do not consider
  990. ;; invisibility obtained through text properties (e.g., link
  991. ;; fontification), as it cannot be toggled.
  992. (let* ((invisible-at-point
  993. (pcase (get-char-property-and-overlay (point) 'invisible)
  994. (`(,_ . ,(and (pred overlayp) o)) o)))
  995. (invisible-before-point
  996. (and (not (bobp))
  997. (pcase (get-char-property-and-overlay (1- (point)) 'invisible)
  998. (`(,_ . ,(and (pred overlayp) o)) o))))
  999. (border-and-ok-direction
  1000. (or
  1001. ;; Check if we are acting predictably before invisible
  1002. ;; text.
  1003. (and invisible-at-point (not invisible-before-point)
  1004. (memq kind '(insert delete-backward)))
  1005. ;; Check if we are acting predictably after invisible text
  1006. ;; This works not well, and I have turned it off. It seems
  1007. ;; better to always show and stop after invisible text.
  1008. ;; (and (not invisible-at-point) invisible-before-point
  1009. ;; (memq kind '(insert delete)))
  1010. )))
  1011. (when (or invisible-at-point invisible-before-point)
  1012. (when (eq org-fold-catch-invisible-edits 'error)
  1013. (user-error "Editing in invisible areas is prohibited, make them visible first"))
  1014. (if (and org-custom-properties-overlays
  1015. (y-or-n-p "Display invisible properties in this buffer? "))
  1016. (org-toggle-custom-properties-visibility)
  1017. ;; Make the area visible
  1018. (save-excursion
  1019. (when (and (not invisible-at-point) invisible-before-point)
  1020. (goto-char
  1021. (previous-single-char-property-change (point) 'invisible)))
  1022. ;; Remove whatever overlay is currently making yet-to-be
  1023. ;; edited text invisible. Also remove nested invisibility
  1024. ;; related overlays.
  1025. (delete-overlay (or invisible-at-point invisible-before-point))
  1026. (let ((origin (if invisible-at-point (point) (1- (point)))))
  1027. (while (pcase (get-char-property-and-overlay origin 'invisible)
  1028. (`(,_ . ,(and (pred overlayp) o))
  1029. (delete-overlay o)
  1030. t)))))
  1031. (cond
  1032. ((eq org-fold-catch-invisible-edits 'show)
  1033. ;; That's it, we do the edit after showing
  1034. (message
  1035. "Unfolding invisible region around point before editing")
  1036. (sit-for 1))
  1037. ((and (eq org-fold-catch-invisible-edits 'smart)
  1038. border-and-ok-direction)
  1039. (message "Unfolding invisible region around point before editing"))
  1040. (t
  1041. ;; Don't do the edit, make the user repeat it in full visibility
  1042. (user-error "Edit in invisible region aborted, repeat to confirm with text visible"))))))))
  1043. (defun org-fold-check-before-invisible-edit--text-properties (kind)
  1044. "Check if editing KIND is dangerous with invisible text around.
  1045. The detailed reaction depends on the user option
  1046. `org-fold-catch-invisible-edits'."
  1047. ;; First, try to get out of here as quickly as possible, to reduce overhead
  1048. (when (and org-fold-catch-invisible-edits
  1049. (or (not (boundp 'visible-mode)) (not visible-mode))
  1050. (or (org-invisible-p)
  1051. (org-invisible-p (max (point-min) (1- (point))))))
  1052. ;; OK, we need to take a closer look. Only consider invisibility
  1053. ;; caused by folding of headlines, drawers, and blocks. Edits
  1054. ;; inside links will be handled by font-lock.
  1055. (let* ((invisible-at-point (org-fold-folded-p (point) '(headline drawer block)))
  1056. (invisible-before-point
  1057. (and (not (bobp))
  1058. (org-fold-folded-p (1- (point)) '(headline drawer block))))
  1059. (border-and-ok-direction
  1060. (or
  1061. ;; Check if we are acting predictably before invisible
  1062. ;; text.
  1063. (and invisible-at-point (not invisible-before-point)
  1064. (memq kind '(insert delete-backward)))
  1065. ;; Check if we are acting predictably after invisible text
  1066. ;; This works not well, and I have turned it off. It seems
  1067. ;; better to always show and stop after invisible text.
  1068. ;; (and (not invisible-at-point) invisible-before-point
  1069. ;; (memq kind '(insert delete)))
  1070. )))
  1071. (when (or invisible-at-point invisible-before-point)
  1072. (when (eq org-fold-catch-invisible-edits 'error)
  1073. (user-error "Editing in invisible areas is prohibited, make them visible first"))
  1074. (if (and org-custom-properties-overlays
  1075. (y-or-n-p "Display invisible properties in this buffer? "))
  1076. (org-toggle-custom-properties-visibility)
  1077. ;; Make the area visible
  1078. (save-excursion
  1079. (org-fold-show-set-visibility 'local))
  1080. (when invisible-before-point
  1081. (org-with-point-at (1- (point)) (org-fold-show-set-visibility 'local)))
  1082. (cond
  1083. ((eq org-fold-catch-invisible-edits 'show)
  1084. ;; That's it, we do the edit after showing
  1085. (message
  1086. "Unfolding invisible region around point before editing")
  1087. (sit-for 1))
  1088. ((and (eq org-fold-catch-invisible-edits 'smart)
  1089. border-and-ok-direction)
  1090. (message "Unfolding invisible region around point before editing"))
  1091. (t
  1092. ;; Don't do the edit, make the user repeat it in full visibility
  1093. (user-error "Edit in invisible region aborted, repeat to confirm with text visible"))))))))
  1094. (defsubst org-fold-check-before-invisible-edit (kind)
  1095. "Check if editing KIND is dangerous with invisible text around.
  1096. The detailed reaction depends on the user option
  1097. `org-fold-catch-invisible-edits'."
  1098. ;; First, try to get out of here as quickly as possible, to reduce overhead
  1099. (if (eq org-fold-core-style 'text-properties)
  1100. (org-fold-check-before-invisible-edit--text-properties kind)
  1101. (org-fold-check-before-invisible-edit--overlays kind)))
  1102. (provide 'org-fold)
  1103. ;;; org-fold.el ends here