emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
blob 3acfbc0cc52c97845247e5508ab6dfe7c73181f9 12578 bytes (raw)
name: lisp/org-struct.el 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
 
;;; org-struct.el --- Org-style editing in non-Org buffers  -*- lexical-binding: t; -*-

;; Copyright (C) 2015 Free Software Foundation, Inc.

;; Author: Carsten Dominik <carsten at orgmode dot org>
;; Keywords: outlines

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; This file provides two minor modes for using Org-style editing
;; commands in buffers that aren't in Org mode. The first mode,
;; orgstruct-mode, defines key-bindings for structural editing
;; commands like inserting headings and manipulating list items. The
;; second minor mode, orgstruct++-mode, builds on the first, adding
;; functions to handle filling and indentation.

;; The basic orgstruct-mode is a bit of a hack: it hijacks all the
;; keys it needs for structure editing, and wraps them in a function
;; (`orgstruct-make-binding') that checks the context around point.
;; If the text around point looks like Org text, the relevant Org
;; command is run.  If it doesn't, the function checks to see if the
;; currently-active major or minor modes have commands bound to those
;; keys, and, if so, calls those commands.

;; The second minor mode, orgstruct++, special-cases various major
;; modes, such as message-mode, to make sure that Org text structures
;; are filled and indented properly.

;; in orgstruct++ mode, we need to set custom values for
;; indent-line-function, adaptive-fill-function,
;; normal-auto-fill-function, and fill-paragraph-function

;;; Code:

(require 'org)

(declare-function message-goto-body "message" ())

(defvar message-cite-prefix-regexp)	; From message.el

(defcustom orgstruct-heading-prefix-regexp ""
  "Regexp that matches the custom prefix of Org headlines in
orgstruct(++)-mode."
  :group 'org
  :version "24.4"
  :package-version '(Org . "8.3")
  :type 'regexp)
;;;###autoload(put 'orgstruct-heading-prefix-regexp 'safe-local-variable 'stringp)

(defcustom orgstruct-setup-hook nil
  "Hook run after orgstruct-mode-map is filled."
  :group 'org
  :version "24.4"
  :package-version '(Org . "8.0")
  :type 'hook)

(defvar orgstruct-initialized nil)

;;;###autoload
(define-minor-mode orgstruct-mode
  "Toggle the minor mode `orgstruct-mode'.
This mode is for using Org-mode structure commands in other
modes.  The following keys behave as if Org-mode were active, if
the cursor is on a headline, or on a plain list item (both as
defined by Org-mode)."
  nil " OrgStruct" (make-sparse-keymap)
  (funcall (if orgstruct-mode
	       'add-to-invisibility-spec
	     'remove-from-invisibility-spec)
	   '(outline . t))
  (when orgstruct-mode
    (org-load-modules-maybe)
    (unless orgstruct-initialized
      (orgstruct-setup)
      (setq orgstruct-initialized t))))

;;;###autoload
(defun turn-on-orgstruct ()
  "Unconditionally turn on `orgstruct-mode'."
  (orgstruct-mode 1))

(defvar org-fb-vars nil)
(make-variable-buffer-local 'org-fb-vars)

(defun orgstruct-error ()
  "Error when there is no default binding for a structure key."
  (interactive)
  (funcall (if (fboundp 'user-error)
	       'user-error
	     'error)
	   "This key has no function outside structure elements"))

(defun orgstruct-setup ()
  "Setup orgstruct keymap."
  (dolist (cell '((org-demote . t)
		  (org-metaleft . t)
		  (org-metaright . t)
		  (org-promote . t)
		  (org-shiftmetaleft . t)
		  (org-shiftmetaright . t)
		  org-backward-element
		  org-backward-heading-same-level
		  org-ctrl-c-ret
		  org-ctrl-c-minus
		  org-ctrl-c-star
		  org-cycle
		  org-forward-heading-same-level
		  org-insert-heading
		  org-insert-heading-respect-content
		  org-kill-note-or-show-branches
		  org-mark-subtree
		  org-meta-return
		  org-metadown
		  org-metaup
		  org-narrow-to-subtree
		  org-promote-subtree
		  org-reveal
		  org-shiftdown
		  org-shiftleft
		  org-shiftmetadown
		  org-shiftmetaup
		  org-shiftright
		  org-shifttab
		  org-shifttab
		  org-shiftup
		  org-show-subtree
		  org-sort
		  org-up-element
		  outline-demote
		  outline-next-visible-heading
		  outline-previous-visible-heading
		  outline-promote
		  outline-up-heading
		  show-children))
    (let ((f (or (car-safe cell) cell))
	  (disable-when-heading-prefix (cdr-safe cell)))
      (when (fboundp f)
	(let ((new-bindings))
	  (dolist (binding (nconc (where-is-internal f org-mode-map)
				  (where-is-internal f outline-mode-map)))
	    (push binding new-bindings)
	    ;; TODO use local-function-key-map
	    (dolist (rep '(("<tab>" . "TAB")
			   ("<return>" . "RET")
			   ("<escape>" . "ESC")
			   ("<delete>" . "DEL")))
	      (setq binding (read-kbd-macro
			     (let ((case-fold-search))
			       (replace-regexp-in-string
				(regexp-quote (cdr rep))
				(car rep)
				(key-description binding)))))
	      (pushnew binding new-bindings :test 'equal)))
	  (dolist (binding new-bindings)
	    (let ((key (lookup-key orgstruct-mode-map binding)))
	      (when (or (not key) (numberp key))
		(ignore-errors
		  (org-defkey orgstruct-mode-map
			      binding
			      (orgstruct-make-binding
			       f binding disable-when-heading-prefix))))))))))
  (run-hooks 'orgstruct-setup-hook))

(defun orgstruct-make-binding (fun key disable-when-heading-prefix)
  "Create a function for binding in the structure minor mode.
FUN is the command to call inside a table.  KEY is the key that
should be checked in for a command to execute outside of tables.
Non-nil `disable-when-heading-prefix' means to disable the command
if `orgstruct-heading-prefix-regexp' is not empty."
  (let ((name (concat "orgstruct-hijacker-" (symbol-name fun))))
    (let ((nname name)
	  (i 0))
      (while (fboundp (intern nname))
	(setq nname (format "%s-%d" name (setq i (1+ i)))))
      (setq name (intern nname)))
    (eval
     (let ((bindings '((org-heading-regexp
			(concat "^"
				orgstruct-heading-prefix-regexp
				"\\(\\*+\\)\\(?: +\\(.*?\\)\\)?[		]*$"))
		       (org-outline-regexp
			(concat orgstruct-heading-prefix-regexp "\\*+ "))
		       (org-outline-regexp-bol
			(concat "^" org-outline-regexp))
		       (outline-regexp org-outline-regexp)
		       (outline-heading-end-regexp "\n")
		       (outline-level 'org-outline-level)
		       (outline-heading-alist))))
       `(defun ,name (arg)
	  ,(concat "In Structure, run `" (symbol-name fun) "'.\n"
		   "Outside of structure, run the binding of `"
		   (key-description key) "'."
		   (when disable-when-heading-prefix
		     (concat
		      "\nIf `orgstruct-heading-prefix-regexp' is not empty, this command will always fall\n"
		      "back to the default binding due to limitations of Org's implementation of\n"
		      "`" (symbol-name fun) "'.")))
	  (interactive "p")
	  (let* ((disable
		  ,(and disable-when-heading-prefix
			'(not (string= orgstruct-heading-prefix-regexp ""))))
		 (fallback
		  (or disable
		      (not
		       (let* ,bindings
			 (org-context-p 'headline 'item
					,(when (memq fun
						     '(org-insert-heading
						       org-insert-heading-respect-content
						       org-meta-return))
					   '(when orgstruct-is-++
					      'item-body))))))))
	    (if fallback
		(let* ((orgstruct-mode)
		       (binding
			(let ((key ,key))
			  (catch 'exit
			    (dolist
				(rep
				 '(nil
				   ("<\\([^>]*\\)tab>" . "\\1TAB")
				   ("<\\([^>]*\\)return>" . "\\1RET")
				   ("<\\([^>]*\\)escape>" . "\\1ESC")
				   ("<\\([^>]*\\)delete>" . "\\1DEL"))
				 nil)
			      (when rep
				(setq key (read-kbd-macro
					   (let ((case-fold-search))
					     (replace-regexp-in-string
					      (car rep)
					      (cdr rep)
					      (key-description key))))))
			      (when (key-binding key)
				(throw 'exit (key-binding key))))))))
		  (if (keymapp binding)
		      (org-set-transient-map binding)
		    (let ((func (or binding
				    (unless disable
				      'orgstruct-error))))
		      (when func
			(call-interactively func)))))
	      (org-run-like-in-org-mode
	       (lambda ()
		 (interactive)
		 (let* ,bindings
		   (call-interactively ',fun)))))))))
    name))

(defun orgstruct++-mode (&optional arg)
  "Toggle `orgstruct-mode', the enhanced version of it.
In addition to setting orgstruct-mode, this also exports all
indentation and autofilling variables from org-mode into the
buffer.  It will also recognize item context in multiline items."
  (interactive "P")
  (setq arg (prefix-numeric-value (or arg (if orgstruct-mode -1 1))))
  (if (< arg 1)
      (progn (orgstruct-mode -1)
	     (mapc (lambda(v)
		     (org-set-local (car v)
				    (if (eq (car-safe (cadr v)) 'quote) (cadadr v) (cadr v))))
		   org-fb-vars))
    (orgstruct-mode 1)
    (setq org-fb-vars nil)
    (unless org-local-vars
      (setq org-local-vars (org-get-local-variables)))
    (let (var val)
      (mapc
       (lambda (x)
	 (when (string-match
		"^\\(paragraph-\\|auto-fill\\|normal-auto-fill\\|fill-paragraph\\|fill-prefix\\|indent-\\)"
		(symbol-name (car x)))
	   (setq var (car x) val (nth 1 x))
	   (push (list var `(quote ,(eval var))) org-fb-vars)
	   (org-set-local var (if (eq (car-safe val) 'quote) (nth 1 val) val))))
       org-local-vars)
      (org-set-local 'fill-paragraph-function 'orgstruct-fill-paragraph)
      (org-set-local 'adaptive-fill-function 'orgstruct-adaptive-fill-function)
      (org-set-local 'auto-fill-function 'orgstruct-auto-fill-function)
      (org-set-local 'indent-line-function 'orgstruct-indent-line-function)
      (org-set-local 'orgstruct-is-++ t))))

(defvar orgstruct-is-++ nil
  "Is `orgstruct-mode' in ++ version in the current-buffer?")
(make-variable-buffer-local 'orgstruct-is-++)

;;;###autoload
(defun turn-on-orgstruct++ ()
  "Unconditionally turn on `orgstruct++-mode'."
  (orgstruct++-mode 1))

(defun orgstruct-fill-paragraph (&optional justify)
  (interactive)
  (cond
   ((and (derived-mode-p 'message-mode)
	 (or (not (message-in-body-p))
	     (save-excursion (move-beginning-of-line 1)
			     (looking-at message-cite-prefix-regexp))))
    ;; Set appropriate variables for message-mode
    (let ((fill-paragraph-function
	   (cadadr (assoc 'fill-paragraph-function org-fb-vars)))
	  (fill-prefix (cadadr (assoc 'fill-prefix org-fb-vars)))
	  (paragraph-start (cadadr (assoc 'paragraph-start org-fb-vars)))
	  (paragraph-separate (cadadr (assoc 'paragraph-separate org-fb-vars)))
	  (org-element-paragraph-separate (cadadr (assoc 'paragraph-separate org-fb-vars))))
      (org-fill-paragraph)))
   (t
    (org-fill-paragraph))))

(defun orgstruct-adaptive-fill-function ()
  "Find the appropriate fill prefix for the current major mode."
  (cond ((derived-mode-p 'message-mode)
	 (save-excursion
	   (beginning-of-line)
	   (cond ((not (message-in-body-p)) nil)
		 ((org-looking-at-p org-table-line-regexp) nil)
		 ((looking-at message-cite-prefix-regexp)
		  (match-string-no-properties 0))
		 ((looking-at org-outline-regexp)
		  (make-string (length (match-string 0)) ?\s))
		 ((message-in-body-p)
		  (let ((fill-paragraph-function
			 (cadadr (assoc 'fill-paragraph-function org-fb-vars)))
			(fill-prefix (cadadr (assoc 'fill-prefix org-fb-vars)))
			(paragraph-start (cadadr (assoc 'paragraph-start org-fb-vars)))
			(paragraph-separate (cadadr (assoc 'paragraph-separate org-fb-vars)))
			(org-element-paragraph-separate (cadadr (assoc 'paragraph-separate org-fb-vars))))
		    (org-adaptive-fill-function))))))
	(t
	 (org-adaptive-fill-function))))

(defun orgstruct-auto-fill-function ()
  (let ((fc (current-fill-column)))
    (when (and fc (> (current-column) fc))
      (let* ((fill-prefix (orgstruct-adaptive-fill-function))
	     ;; Enforce empty fill prefix, if required.  Otherwise, it
	     ;; will be computed again.
	     (adaptive-fill-mode (not (equal fill-prefix ""))))
	(when fill-prefix (do-auto-fill))))))

(defun orgstruct-indent-line-function ()
  (interactive)
  (cond
   (orgstruct-is-++
    (let ((indent-line-function
	   (cadadr (assq 'indent-line-function org-fb-vars))))
      (indent-according-to-mode)))
   (t
    (org-indent-line))))

(provide 'org-struct)
;;; org-struct.el ends here

debug log:

solving 3acfbc0 ...
found 3acfbc0 in https://list.orgmode.org/orgmode/871tkhnnpp.fsf@ericabrahamsen.net/

applying [1/1] https://list.orgmode.org/orgmode/871tkhnnpp.fsf@ericabrahamsen.net/
diff --git a/lisp/org-struct.el b/lisp/org-struct.el
new file mode 100644
index 0000000..3acfbc0

Checking patch lisp/org-struct.el...
Applied patch lisp/org-struct.el cleanly.

index at:
100644 3acfbc0cc52c97845247e5508ab6dfe7c73181f9	lisp/org-struct.el

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).